xref: /illumos-gate/usr/src/uts/sun4u/opl/io/mc-opl.c (revision 0cc8ae86)
125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2125cf1a30Sjl /*
2225cf1a30Sjl  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
2325cf1a30Sjl  */
2425cf1a30Sjl 
2525cf1a30Sjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
2625cf1a30Sjl 
2725cf1a30Sjl #include <sys/types.h>
2825cf1a30Sjl #include <sys/sysmacros.h>
2925cf1a30Sjl #include <sys/conf.h>
3025cf1a30Sjl #include <sys/modctl.h>
3125cf1a30Sjl #include <sys/stat.h>
3225cf1a30Sjl #include <sys/async.h>
3325cf1a30Sjl #include <sys/machsystm.h>
34*0cc8ae86Sav #include <sys/promif.h>
3525cf1a30Sjl #include <sys/ksynch.h>
3625cf1a30Sjl #include <sys/ddi.h>
3725cf1a30Sjl #include <sys/sunddi.h>
3825cf1a30Sjl #include <sys/ddifm.h>
3925cf1a30Sjl #include <sys/fm/protocol.h>
4025cf1a30Sjl #include <sys/fm/util.h>
4125cf1a30Sjl #include <sys/kmem.h>
4225cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h>
4325cf1a30Sjl #include <sys/memlist.h>
4425cf1a30Sjl #include <sys/param.h>
45*0cc8ae86Sav #include <sys/disp.h>
4625cf1a30Sjl #include <sys/ontrap.h>
4725cf1a30Sjl #include <vm/page.h>
4825cf1a30Sjl #include <sys/mc-opl.h>
49*0cc8ae86Sav #include <sys/opl.h>
50*0cc8ae86Sav #include <sys/opl_dimm.h>
51*0cc8ae86Sav #include <sys/scfd/scfostoescf.h>
5225cf1a30Sjl 
5325cf1a30Sjl /*
5425cf1a30Sjl  * Function prototypes
5525cf1a30Sjl  */
5625cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *);
5725cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *);
5825cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
5925cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
6025cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
6125cf1a30Sjl 
62*0cc8ae86Sav static int mc_poll_init(void);
63*0cc8ae86Sav static void mc_poll_fini(void);
6425cf1a30Sjl static int mc_board_add(mc_opl_t *mcp);
6525cf1a30Sjl static int mc_board_del(mc_opl_t *mcp);
6625cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag);
6725cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag);
68*0cc8ae86Sav int opl_mc_suspend(void);
69*0cc8ae86Sav int opl_mc_resume(void);
7025cf1a30Sjl 
7125cf1a30Sjl static void insert_mcp(mc_opl_t *mcp);
7225cf1a30Sjl static void delete_mcp(mc_opl_t *mcp);
7325cf1a30Sjl 
7425cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr);
7525cf1a30Sjl 
7625cf1a30Sjl static int mc_valid_pa(mc_opl_t *mcp, uint64_t pa);
7725cf1a30Sjl 
7825cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *);
79*0cc8ae86Sav int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr);
80*0cc8ae86Sav int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
81*0cc8ae86Sav int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp);
82*0cc8ae86Sav int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
83*0cc8ae86Sav     int buflen, int *lenp);
84*0cc8ae86Sav mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp);
85*0cc8ae86Sav mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp);
86*0cc8ae86Sav int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank,
87*0cc8ae86Sav     uint32_t mf_type, uint32_t d_slot);
88*0cc8ae86Sav static void mc_free_dimm_list(mc_dimm_info_t *d);
8925cf1a30Sjl static void mc_get_mlist(mc_opl_t *);
90*0cc8ae86Sav static void mc_polling(void);
91*0cc8ae86Sav static int mc_opl_get_physical_board(int);
92*0cc8ae86Sav 
93*0cc8ae86Sav #ifdef	DEBUG
94*0cc8ae86Sav static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *);
95*0cc8ae86Sav void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz);
96*0cc8ae86Sav void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp);
97*0cc8ae86Sav #endif
9825cf1a30Sjl 
9925cf1a30Sjl #pragma weak opl_get_physical_board
10025cf1a30Sjl extern int opl_get_physical_board(int);
101*0cc8ae86Sav extern int plat_max_boards(void);
10225cf1a30Sjl 
10325cf1a30Sjl /*
10425cf1a30Sjl  * Configuration data structures
10525cf1a30Sjl  */
10625cf1a30Sjl static struct cb_ops mc_cb_ops = {
10725cf1a30Sjl 	mc_open,			/* open */
10825cf1a30Sjl 	mc_close,			/* close */
10925cf1a30Sjl 	nulldev,			/* strategy */
11025cf1a30Sjl 	nulldev,			/* print */
11125cf1a30Sjl 	nodev,				/* dump */
11225cf1a30Sjl 	nulldev,			/* read */
11325cf1a30Sjl 	nulldev,			/* write */
11425cf1a30Sjl 	mc_ioctl,			/* ioctl */
11525cf1a30Sjl 	nodev,				/* devmap */
11625cf1a30Sjl 	nodev,				/* mmap */
11725cf1a30Sjl 	nodev,				/* segmap */
11825cf1a30Sjl 	nochpoll,			/* poll */
11925cf1a30Sjl 	ddi_prop_op,			/* cb_prop_op */
12025cf1a30Sjl 	0,				/* streamtab */
12125cf1a30Sjl 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
12225cf1a30Sjl 	CB_REV,				/* rev */
12325cf1a30Sjl 	nodev,				/* cb_aread */
12425cf1a30Sjl 	nodev				/* cb_awrite */
12525cf1a30Sjl };
12625cf1a30Sjl 
12725cf1a30Sjl static struct dev_ops mc_ops = {
12825cf1a30Sjl 	DEVO_REV,			/* rev */
12925cf1a30Sjl 	0,				/* refcnt  */
13025cf1a30Sjl 	ddi_getinfo_1to1,		/* getinfo */
13125cf1a30Sjl 	nulldev,			/* identify */
13225cf1a30Sjl 	nulldev,			/* probe */
13325cf1a30Sjl 	mc_attach,			/* attach */
13425cf1a30Sjl 	mc_detach,			/* detach */
13525cf1a30Sjl 	nulldev,			/* reset */
13625cf1a30Sjl 	&mc_cb_ops,			/* cb_ops */
13725cf1a30Sjl 	(struct bus_ops *)0,		/* bus_ops */
13825cf1a30Sjl 	nulldev				/* power */
13925cf1a30Sjl };
14025cf1a30Sjl 
14125cf1a30Sjl /*
14225cf1a30Sjl  * Driver globals
14325cf1a30Sjl  */
14425cf1a30Sjl 
145*0cc8ae86Sav static enum {
146*0cc8ae86Sav 	MODEL_FF1 = 0,
147*0cc8ae86Sav 	MODEL_FF2 = 1,
148*0cc8ae86Sav 	MODEL_DC = 2
149*0cc8ae86Sav } plat_model = MODEL_DC;	/* The default behaviour is DC */
150*0cc8ae86Sav 
151*0cc8ae86Sav static struct plat_model_names {
152*0cc8ae86Sav 	const char *unit_name;
153*0cc8ae86Sav 	const char *mem_name;
154*0cc8ae86Sav } model_names[] = {
155*0cc8ae86Sav 	{ "MBU_A", "MEMB" },
156*0cc8ae86Sav 	{ "MBU_B", "MEMB" },
157*0cc8ae86Sav 	{ "CMU", "" }
158*0cc8ae86Sav };
15925cf1a30Sjl 
160*0cc8ae86Sav /*
161*0cc8ae86Sav  * The DIMM Names for DC platform.
162*0cc8ae86Sav  * The index into this table is made up of (bank, dslot),
163*0cc8ae86Sav  * Where dslot occupies bits 0-1 and bank occupies 2-4.
164*0cc8ae86Sav  */
165*0cc8ae86Sav static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = {
166*0cc8ae86Sav 	/* --------CMUnn----------- */
167*0cc8ae86Sav 	/* --CS0-----|--CS1------ */
168*0cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
169*0cc8ae86Sav 	"03A", "02A", "03B", "02B", /* MAC 0 bank 0 */
170*0cc8ae86Sav 	"13A", "12A", "13B", "12B", /* MAC 0 bank 1 */
171*0cc8ae86Sav 	"23A", "22A", "23B", "22B", /* MAC 1 bank 2 */
172*0cc8ae86Sav 	"33A", "32A", "33B", "32B", /* MAC 1 bank 3 */
173*0cc8ae86Sav 	"01A", "00A", "01B", "00B", /* MAC 2 bank 4 */
174*0cc8ae86Sav 	"11A", "10A", "11B", "10B", /* MAC 2 bank 5 */
175*0cc8ae86Sav 	"21A", "20A", "21B", "20B", /* MAC 3 bank 6 */
176*0cc8ae86Sav 	"31A", "30A", "31B", "30B"  /* MAC 3 bank 7 */
177*0cc8ae86Sav };
178*0cc8ae86Sav 
179*0cc8ae86Sav /*
180*0cc8ae86Sav  * The DIMM Names for FF1/FF2 platforms.
181*0cc8ae86Sav  * The index into this table is made up of (board, bank, dslot),
182*0cc8ae86Sav  * Where dslot occupies bits 0-1, bank occupies 2-4 and
183*0cc8ae86Sav  * board occupies the bit 5.
184*0cc8ae86Sav  */
185*0cc8ae86Sav static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = {
186*0cc8ae86Sav 	/* --------CMU0---------- */
187*0cc8ae86Sav 	/* --CS0-----|--CS1------ */
188*0cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
189*0cc8ae86Sav 	"03A", "02A", "03B", "02B", /* MAC 0 bank 0 */
190*0cc8ae86Sav 	"01A", "00A", "01B", "00B", /* MAC 0 bank 1 */
191*0cc8ae86Sav 	"13A", "12A", "13B", "12B", /* MAC 1 bank 2 */
192*0cc8ae86Sav 	"11A", "10A", "11B", "10B", /* MAC 1 bank 3 */
193*0cc8ae86Sav 	"23A", "20A", "23B", "20B", /* MAC 2 bank 4 */
194*0cc8ae86Sav 	"21A", "20A", "21B", "20B", /* MAC 2 bank 5 */
195*0cc8ae86Sav 	"33A", "32A", "33B", "32B", /* MAC 3 bank 6 */
196*0cc8ae86Sav 	"31A", "30A", "31B", "30B", /* MAC 3 bank 7 */
197*0cc8ae86Sav 	/* --------CMU1---------- */
198*0cc8ae86Sav 	/* --CS0-----|--CS1------ */
199*0cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
200*0cc8ae86Sav 	"43A", "42A", "43B", "42B", /* MAC 0 bank 0 */
201*0cc8ae86Sav 	"41A", "40A", "41B", "40B", /* MAC 0 bank 1 */
202*0cc8ae86Sav 	"53A", "52A", "53B", "50B", /* MAC 1 bank 2 */
203*0cc8ae86Sav 	"51A", "50A", "51B", "50B", /* MAC 1 bank 3 */
204*0cc8ae86Sav 	"63A", "62A", "63B", "62B", /* MAC 2 bank 4 */
205*0cc8ae86Sav 	"61A", "60A", "61B", "60B", /* MAC 2 bank 5 */
206*0cc8ae86Sav 	"73A", "72A", "73B", "72B", /* MAC 3 bank 6 */
207*0cc8ae86Sav 	"71A", "70A", "71B", "70B"  /* MAC 3 bank 7 */
208*0cc8ae86Sav };
209*0cc8ae86Sav 
210*0cc8ae86Sav #define	BD_BK_SLOT_TO_INDEX(bd, bk, s)			\
211*0cc8ae86Sav 	(((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03))
212*0cc8ae86Sav 
213*0cc8ae86Sav #define	INDEX_TO_BANK(i)			(((i) & 0x1C) >> 2)
214*0cc8ae86Sav #define	INDEX_TO_SLOT(i)			((i) & 0x03)
215*0cc8ae86Sav 
216*0cc8ae86Sav /* Isolation unit size is 64 MB */
217*0cc8ae86Sav #define	MC_ISOLATION_BSIZE	(64 * 1024 * 1024)
218*0cc8ae86Sav 
219*0cc8ae86Sav #define	MC_MAX_SPEEDS 7
220*0cc8ae86Sav 
221*0cc8ae86Sav /* make this a structure */
222*0cc8ae86Sav 
223*0cc8ae86Sav typedef struct {
224*0cc8ae86Sav 	uint32_t mc_speeds;
225*0cc8ae86Sav 	uint32_t mc_period;
226*0cc8ae86Sav } mc_scan_speed_t;
227*0cc8ae86Sav 
228*0cc8ae86Sav #define	MC_CNTL_SPEED_SHIFT 26
229*0cc8ae86Sav 
230*0cc8ae86Sav static mc_scan_speed_t	mc_scan_speeds[MC_MAX_SPEEDS] = {
231*0cc8ae86Sav 	{0x6 << MC_CNTL_SPEED_SHIFT, 0},
232*0cc8ae86Sav 	{0x5 << MC_CNTL_SPEED_SHIFT, 32},
233*0cc8ae86Sav 	{0x4 << MC_CNTL_SPEED_SHIFT, 64},
234*0cc8ae86Sav 	{0x3 << MC_CNTL_SPEED_SHIFT, 128},
235*0cc8ae86Sav 	{0x2 << MC_CNTL_SPEED_SHIFT, 256},
236*0cc8ae86Sav 	{0x1 << MC_CNTL_SPEED_SHIFT, 512},
237*0cc8ae86Sav 	{0x0 << MC_CNTL_SPEED_SHIFT, 1024}
238*0cc8ae86Sav };
239*0cc8ae86Sav 
240*0cc8ae86Sav static uint32_t	mc_max_speed = (0x6 << 26);
241*0cc8ae86Sav 
242*0cc8ae86Sav /* we have to measure these delays */
243*0cc8ae86Sav 
244*0cc8ae86Sav int mc_isolation_bsize = MC_ISOLATION_BSIZE;
245*0cc8ae86Sav int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC;
246*0cc8ae86Sav int mc_max_scf_retry = 16;
247*0cc8ae86Sav int mc_max_scf_logs = 64;
248*0cc8ae86Sav int mc_max_errlog_processed = BANKNUM_PER_SB*2;
249*0cc8ae86Sav int mc_scan_period = 12 * 60 * 60;	/* 12 hours period */
250*0cc8ae86Sav int mc_max_rewrite_loop = 100;
251*0cc8ae86Sav int mc_rewrite_delay = 10;
252*0cc8ae86Sav /*
253*0cc8ae86Sav  * it takes SCF about 300 m.s. to process a requst.  We can bail out
254*0cc8ae86Sav  * if it is busy.  It does not pay to wait for it too long.
255*0cc8ae86Sav  */
256*0cc8ae86Sav int mc_max_scf_loop = 2;
257*0cc8ae86Sav int mc_scf_delay = 100;
258*0cc8ae86Sav int mc_pce_dropped = 0;
259*0cc8ae86Sav int mc_poll_priority = MINCLSYSPRI;
26025cf1a30Sjl 
261*0cc8ae86Sav 
262*0cc8ae86Sav /*
263*0cc8ae86Sav  * Mutex heierachy in mc-opl
264*0cc8ae86Sav  * If both mcmutex and mc_lock must be held,
265*0cc8ae86Sav  * mcmutex must be acquired first, and then mc_lock.
266*0cc8ae86Sav  */
267*0cc8ae86Sav 
268*0cc8ae86Sav static kmutex_t mcmutex;
269*0cc8ae86Sav mc_opl_t *mc_instances[OPL_MAX_BOARDS];
270*0cc8ae86Sav 
271*0cc8ae86Sav static kmutex_t mc_polling_lock;
272*0cc8ae86Sav static kcondvar_t mc_polling_cv;
273*0cc8ae86Sav static kcondvar_t mc_poll_exit_cv;
274*0cc8ae86Sav static int mc_poll_cmd = 0;
275*0cc8ae86Sav static int mc_pollthr_running = 0;
276*0cc8ae86Sav int mc_timeout_period = 0; /* this is in m.s. */
27725cf1a30Sjl void *mc_statep;
27825cf1a30Sjl 
27925cf1a30Sjl #ifdef	DEBUG
2802742aa22Shyw int oplmc_debug = 0;
28125cf1a30Sjl #endif
28225cf1a30Sjl 
283*0cc8ae86Sav static int mc_debug_show_all = 0;
28425cf1a30Sjl 
28525cf1a30Sjl extern struct mod_ops mod_driverops;
28625cf1a30Sjl 
28725cf1a30Sjl static struct modldrv modldrv = {
28825cf1a30Sjl 	&mod_driverops,			/* module type, this one is a driver */
289*0cc8ae86Sav 	"OPL Memory-controller %I%",	/* module name */
29025cf1a30Sjl 	&mc_ops,			/* driver ops */
29125cf1a30Sjl };
29225cf1a30Sjl 
29325cf1a30Sjl static struct modlinkage modlinkage = {
29425cf1a30Sjl 	MODREV_1,		/* rev */
29525cf1a30Sjl 	(void *)&modldrv,
29625cf1a30Sjl 	NULL
29725cf1a30Sjl };
29825cf1a30Sjl 
29925cf1a30Sjl #pragma weak opl_get_mem_unum
300*0cc8ae86Sav #pragma weak opl_get_mem_sid
301*0cc8ae86Sav #pragma weak opl_get_mem_offset
302*0cc8ae86Sav #pragma weak opl_get_mem_addr
303*0cc8ae86Sav 
30425cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *);
305*0cc8ae86Sav extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp);
306*0cc8ae86Sav extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp);
307*0cc8ae86Sav extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset,
308*0cc8ae86Sav     uint64_t *paddr);
309*0cc8ae86Sav 
31025cf1a30Sjl 
31125cf1a30Sjl /*
31225cf1a30Sjl  * pseudo-mc node portid format
31325cf1a30Sjl  *
31425cf1a30Sjl  *		[10]   = 0
31525cf1a30Sjl  *		[9]    = 1
31625cf1a30Sjl  *		[8]    = LSB_ID[4] = 0
31725cf1a30Sjl  *		[7:4]  = LSB_ID[3:0]
31825cf1a30Sjl  *		[3:0]  = 0
31925cf1a30Sjl  *
32025cf1a30Sjl  */
32125cf1a30Sjl 
32225cf1a30Sjl /*
32325cf1a30Sjl  * These are the module initialization routines.
32425cf1a30Sjl  */
32525cf1a30Sjl int
32625cf1a30Sjl _init(void)
32725cf1a30Sjl {
328*0cc8ae86Sav 	int	error;
329*0cc8ae86Sav 	int	plen;
330*0cc8ae86Sav 	char	model[20];
331*0cc8ae86Sav 	pnode_t	node;
33225cf1a30Sjl 
33325cf1a30Sjl 
33425cf1a30Sjl 	if ((error = ddi_soft_state_init(&mc_statep,
33525cf1a30Sjl 	    sizeof (mc_opl_t), 1)) != 0)
33625cf1a30Sjl 		return (error);
33725cf1a30Sjl 
338*0cc8ae86Sav 	if ((error = mc_poll_init()) != 0) {
339*0cc8ae86Sav 		ddi_soft_state_fini(&mc_statep);
340*0cc8ae86Sav 		return (error);
341*0cc8ae86Sav 	}
342*0cc8ae86Sav 
34325cf1a30Sjl 	mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
34425cf1a30Sjl 	if (&opl_get_mem_unum)
34525cf1a30Sjl 		opl_get_mem_unum = mc_get_mem_unum;
346*0cc8ae86Sav 	if (&opl_get_mem_sid)
347*0cc8ae86Sav 		opl_get_mem_sid = mc_get_mem_sid;
348*0cc8ae86Sav 	if (&opl_get_mem_offset)
349*0cc8ae86Sav 		opl_get_mem_offset = mc_get_mem_offset;
350*0cc8ae86Sav 	if (&opl_get_mem_addr)
351*0cc8ae86Sav 		opl_get_mem_addr = mc_get_mem_addr;
352*0cc8ae86Sav 
353*0cc8ae86Sav 	node = prom_rootnode();
354*0cc8ae86Sav 	plen = prom_getproplen(node, "model");
355*0cc8ae86Sav 
356*0cc8ae86Sav 	if (plen > 0 && plen < sizeof (model)) {
357*0cc8ae86Sav 		(void) prom_getprop(node, "model", model);
358*0cc8ae86Sav 		model[plen] = '\0';
359*0cc8ae86Sav 		if (strcmp(model, "FF1") == 0)
360*0cc8ae86Sav 			plat_model = MODEL_FF1;
361*0cc8ae86Sav 		else if (strcmp(model, "FF2") == 0)
362*0cc8ae86Sav 			plat_model = MODEL_FF2;
363*0cc8ae86Sav 		else if (strncmp(model, "DC", 2) == 0)
364*0cc8ae86Sav 			plat_model = MODEL_DC;
365*0cc8ae86Sav 	}
36625cf1a30Sjl 
36725cf1a30Sjl 	error =  mod_install(&modlinkage);
36825cf1a30Sjl 	if (error != 0) {
36925cf1a30Sjl 		if (&opl_get_mem_unum)
37025cf1a30Sjl 			opl_get_mem_unum = NULL;
371*0cc8ae86Sav 		if (&opl_get_mem_sid)
372*0cc8ae86Sav 			opl_get_mem_sid = NULL;
373*0cc8ae86Sav 		if (&opl_get_mem_offset)
374*0cc8ae86Sav 			opl_get_mem_offset = NULL;
375*0cc8ae86Sav 		if (&opl_get_mem_addr)
376*0cc8ae86Sav 			opl_get_mem_addr = NULL;
37725cf1a30Sjl 		mutex_destroy(&mcmutex);
378*0cc8ae86Sav 		mc_poll_fini();
37925cf1a30Sjl 		ddi_soft_state_fini(&mc_statep);
38025cf1a30Sjl 	}
38125cf1a30Sjl 	return (error);
38225cf1a30Sjl }
38325cf1a30Sjl 
38425cf1a30Sjl int
38525cf1a30Sjl _fini(void)
38625cf1a30Sjl {
38725cf1a30Sjl 	int error;
38825cf1a30Sjl 
38925cf1a30Sjl 	if ((error = mod_remove(&modlinkage)) != 0)
39025cf1a30Sjl 		return (error);
39125cf1a30Sjl 
39225cf1a30Sjl 	if (&opl_get_mem_unum)
39325cf1a30Sjl 		opl_get_mem_unum = NULL;
394*0cc8ae86Sav 	if (&opl_get_mem_sid)
395*0cc8ae86Sav 		opl_get_mem_sid = NULL;
396*0cc8ae86Sav 	if (&opl_get_mem_offset)
397*0cc8ae86Sav 		opl_get_mem_offset = NULL;
398*0cc8ae86Sav 	if (&opl_get_mem_addr)
399*0cc8ae86Sav 		opl_get_mem_addr = NULL;
40025cf1a30Sjl 
401*0cc8ae86Sav 	mutex_destroy(&mcmutex);
402*0cc8ae86Sav 	mc_poll_fini();
40325cf1a30Sjl 	ddi_soft_state_fini(&mc_statep);
40425cf1a30Sjl 
40525cf1a30Sjl 	return (0);
40625cf1a30Sjl }
40725cf1a30Sjl 
40825cf1a30Sjl int
40925cf1a30Sjl _info(struct modinfo *modinfop)
41025cf1a30Sjl {
41125cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
41225cf1a30Sjl }
41325cf1a30Sjl 
414*0cc8ae86Sav static void
415*0cc8ae86Sav mc_polling_thread()
416*0cc8ae86Sav {
417*0cc8ae86Sav 	mutex_enter(&mc_polling_lock);
418*0cc8ae86Sav 	mc_pollthr_running = 1;
419*0cc8ae86Sav 	while (!(mc_poll_cmd & MC_POLL_EXIT)) {
420*0cc8ae86Sav 		mc_polling();
421*0cc8ae86Sav 		cv_timedwait(&mc_polling_cv, &mc_polling_lock,
422*0cc8ae86Sav 		    ddi_get_lbolt() + mc_timeout_period);
423*0cc8ae86Sav 	}
424*0cc8ae86Sav 	mc_pollthr_running = 0;
425*0cc8ae86Sav 
426*0cc8ae86Sav 	/*
427*0cc8ae86Sav 	 * signal if any one is waiting for this thread to exit.
428*0cc8ae86Sav 	 */
429*0cc8ae86Sav 	cv_signal(&mc_poll_exit_cv);
430*0cc8ae86Sav 	mutex_exit(&mc_polling_lock);
431*0cc8ae86Sav 	thread_exit();
432*0cc8ae86Sav 	/* NOTREACHED */
433*0cc8ae86Sav }
434*0cc8ae86Sav 
435*0cc8ae86Sav static int
436*0cc8ae86Sav mc_poll_init()
437*0cc8ae86Sav {
438*0cc8ae86Sav 	mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL);
439*0cc8ae86Sav 	cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL);
440*0cc8ae86Sav 	cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL);
441*0cc8ae86Sav 	return (0);
442*0cc8ae86Sav }
443*0cc8ae86Sav 
444*0cc8ae86Sav static void
445*0cc8ae86Sav mc_poll_fini()
446*0cc8ae86Sav {
447*0cc8ae86Sav 	mutex_enter(&mc_polling_lock);
448*0cc8ae86Sav 	if (mc_pollthr_running) {
449*0cc8ae86Sav 		mc_poll_cmd = MC_POLL_EXIT;
450*0cc8ae86Sav 		cv_signal(&mc_polling_cv);
451*0cc8ae86Sav 		while (mc_pollthr_running) {
452*0cc8ae86Sav 			cv_wait(&mc_poll_exit_cv, &mc_polling_lock);
453*0cc8ae86Sav 		}
454*0cc8ae86Sav 	}
455*0cc8ae86Sav 	mutex_exit(&mc_polling_lock);
456*0cc8ae86Sav 	mutex_destroy(&mc_polling_lock);
457*0cc8ae86Sav 	cv_destroy(&mc_polling_cv);
458*0cc8ae86Sav 	cv_destroy(&mc_poll_exit_cv);
459*0cc8ae86Sav }
460*0cc8ae86Sav 
46125cf1a30Sjl static int
46225cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
46325cf1a30Sjl {
46425cf1a30Sjl 	mc_opl_t *mcp;
46525cf1a30Sjl 	int instance;
466*0cc8ae86Sav 	int rv;
46725cf1a30Sjl 
46825cf1a30Sjl 	/* get the instance of this devi */
46925cf1a30Sjl 	instance = ddi_get_instance(devi);
47025cf1a30Sjl 
47125cf1a30Sjl 	switch (cmd) {
47225cf1a30Sjl 	case DDI_ATTACH:
47325cf1a30Sjl 		break;
47425cf1a30Sjl 	case DDI_RESUME:
47525cf1a30Sjl 		mcp = ddi_get_soft_state(mc_statep, instance);
476*0cc8ae86Sav 		rv = mc_resume(mcp, MC_DRIVER_SUSPENDED);
477*0cc8ae86Sav 		return (rv);
47825cf1a30Sjl 	default:
47925cf1a30Sjl 		return (DDI_FAILURE);
48025cf1a30Sjl 	}
48125cf1a30Sjl 
48225cf1a30Sjl 	if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS)
48325cf1a30Sjl 		return (DDI_FAILURE);
48425cf1a30Sjl 
48525cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
48625cf1a30Sjl 		goto bad;
48725cf1a30Sjl 	}
48825cf1a30Sjl 
489*0cc8ae86Sav 	if (mc_timeout_period == 0) {
490*0cc8ae86Sav 		mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi,
491*0cc8ae86Sav 			DDI_PROP_DONTPASS, "mc-timeout-interval-sec",
492*0cc8ae86Sav 			mc_patrol_interval_sec);
493*0cc8ae86Sav 		mc_timeout_period = drv_usectohz(
494*0cc8ae86Sav 			1000000 * mc_patrol_interval_sec / OPL_MAX_BOARDS);
495*0cc8ae86Sav 	}
496*0cc8ae86Sav 
49725cf1a30Sjl 	/* set informations in mc state */
49825cf1a30Sjl 	mcp->mc_dip = devi;
49925cf1a30Sjl 
50025cf1a30Sjl 	if (mc_board_add(mcp))
50125cf1a30Sjl 		goto bad;
50225cf1a30Sjl 
50325cf1a30Sjl 	insert_mcp(mcp);
504*0cc8ae86Sav 
505*0cc8ae86Sav 	/*
506*0cc8ae86Sav 	 * Start the polling thread if it is not running already.
507*0cc8ae86Sav 	 */
508*0cc8ae86Sav 	mutex_enter(&mc_polling_lock);
509*0cc8ae86Sav 	if (!mc_pollthr_running) {
510*0cc8ae86Sav 		(void) thread_create(NULL, 0, (void (*)())mc_polling_thread,
511*0cc8ae86Sav 			NULL, 0, &p0, TS_RUN, mc_poll_priority);
512*0cc8ae86Sav 	}
513*0cc8ae86Sav 	mutex_exit(&mc_polling_lock);
51425cf1a30Sjl 	ddi_report_dev(devi);
51525cf1a30Sjl 
51625cf1a30Sjl 	return (DDI_SUCCESS);
51725cf1a30Sjl 
51825cf1a30Sjl bad:
51925cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
52025cf1a30Sjl 	return (DDI_FAILURE);
52125cf1a30Sjl }
52225cf1a30Sjl 
52325cf1a30Sjl /* ARGSUSED */
52425cf1a30Sjl static int
52525cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
52625cf1a30Sjl {
527*0cc8ae86Sav 	int rv;
52825cf1a30Sjl 	int instance;
52925cf1a30Sjl 	mc_opl_t *mcp;
53025cf1a30Sjl 
53125cf1a30Sjl 	/* get the instance of this devi */
53225cf1a30Sjl 	instance = ddi_get_instance(devi);
53325cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
53425cf1a30Sjl 		return (DDI_FAILURE);
53525cf1a30Sjl 	}
53625cf1a30Sjl 
53725cf1a30Sjl 	switch (cmd) {
53825cf1a30Sjl 	case DDI_SUSPEND:
539*0cc8ae86Sav 		rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED);
540*0cc8ae86Sav 		return (rv);
54125cf1a30Sjl 	case DDI_DETACH:
54225cf1a30Sjl 		break;
54325cf1a30Sjl 	default:
54425cf1a30Sjl 		return (DDI_FAILURE);
54525cf1a30Sjl 	}
54625cf1a30Sjl 
547*0cc8ae86Sav 	delete_mcp(mcp);
54825cf1a30Sjl 	if (mc_board_del(mcp) != DDI_SUCCESS) {
54925cf1a30Sjl 		return (DDI_FAILURE);
55025cf1a30Sjl 	}
55125cf1a30Sjl 
55225cf1a30Sjl 	/* free up the soft state */
55325cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
55425cf1a30Sjl 
55525cf1a30Sjl 	return (DDI_SUCCESS);
55625cf1a30Sjl }
55725cf1a30Sjl 
55825cf1a30Sjl /* ARGSUSED */
55925cf1a30Sjl static int
56025cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
56125cf1a30Sjl {
56225cf1a30Sjl 	return (0);
56325cf1a30Sjl }
56425cf1a30Sjl 
56525cf1a30Sjl /* ARGSUSED */
56625cf1a30Sjl static int
56725cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
56825cf1a30Sjl {
56925cf1a30Sjl 	return (0);
57025cf1a30Sjl }
57125cf1a30Sjl 
57225cf1a30Sjl /* ARGSUSED */
57325cf1a30Sjl static int
57425cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
57525cf1a30Sjl 	int *rvalp)
57625cf1a30Sjl {
577*0cc8ae86Sav #ifdef DEBUG
578*0cc8ae86Sav 	return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp));
579*0cc8ae86Sav #else
58025cf1a30Sjl 	return (ENXIO);
581*0cc8ae86Sav #endif
58225cf1a30Sjl }
58325cf1a30Sjl 
58425cf1a30Sjl /*
58525cf1a30Sjl  * PA validity check:
58625cf1a30Sjl  * This function return 1 if the PA is valid, otherwise
58725cf1a30Sjl  * return 0.
58825cf1a30Sjl  */
58925cf1a30Sjl 
59025cf1a30Sjl /* ARGSUSED */
59125cf1a30Sjl static int
59225cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr)
59325cf1a30Sjl {
59425cf1a30Sjl 	/*
59525cf1a30Sjl 	 * Check if the addr is on the board.
59625cf1a30Sjl 	 */
59725cf1a30Sjl 	if ((addr < mcp->mc_start_address) ||
59825cf1a30Sjl 	    (mcp->mc_start_address + mcp->mc_size <= addr))
59925cf1a30Sjl 		return (0);
60025cf1a30Sjl 
60125cf1a30Sjl 	if (mcp->mlist == NULL)
60225cf1a30Sjl 		mc_get_mlist(mcp);
60325cf1a30Sjl 
60425cf1a30Sjl 	if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) {
60525cf1a30Sjl 		return (1);
60625cf1a30Sjl 	}
60725cf1a30Sjl 	return (0);
60825cf1a30Sjl }
60925cf1a30Sjl 
61025cf1a30Sjl /*
61125cf1a30Sjl  * mac-pa translation routines.
61225cf1a30Sjl  *
61325cf1a30Sjl  *    Input: mc driver state, (LSB#, Bank#, DIMM address)
61425cf1a30Sjl  *    Output: physical address
61525cf1a30Sjl  *
61625cf1a30Sjl  *    Valid   - return value:  0
61725cf1a30Sjl  *    Invalid - return value: -1
61825cf1a30Sjl  */
61925cf1a30Sjl static int
62025cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa)
62125cf1a30Sjl {
62225cf1a30Sjl 	int i;
62325cf1a30Sjl 	uint64_t pa_offset = 0;
62425cf1a30Sjl 	int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1;
62525cf1a30Sjl 	int bank = maddr->ma_bank;
62625cf1a30Sjl 	mc_addr_t maddr1;
62725cf1a30Sjl 	int bank0, bank1;
62825cf1a30Sjl 
62925cf1a30Sjl 	MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
63025cf1a30Sjl 		maddr->ma_dimm_addr);
63125cf1a30Sjl 
63225cf1a30Sjl 	/* loc validity check */
63325cf1a30Sjl 	ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd);
63425cf1a30Sjl 	ASSERT(bank >= 0 && OPL_BANK_MAX > bank);
63525cf1a30Sjl 
63625cf1a30Sjl 	/* Do translation */
63725cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
63825cf1a30Sjl 		int pa_bit = 0;
63925cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
64025cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
64125cf1a30Sjl 			pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1;
64225cf1a30Sjl 		} else if (mc_bit == MP_NONE) {
64325cf1a30Sjl 			pa_bit = 0;
64425cf1a30Sjl 		} else if (mc_bit == MP_BANK_0) {
64525cf1a30Sjl 			pa_bit = bank & 1;
64625cf1a30Sjl 		} else if (mc_bit == MP_BANK_1) {
64725cf1a30Sjl 			pa_bit = (bank >> 1) & 1;
64825cf1a30Sjl 		} else if (mc_bit == MP_BANK_2) {
64925cf1a30Sjl 			pa_bit = (bank >> 2) & 1;
65025cf1a30Sjl 		}
65125cf1a30Sjl 		pa_offset |= ((uint64_t)pa_bit) << i;
65225cf1a30Sjl 	}
65325cf1a30Sjl 	*pa = mcp->mc_start_address + pa_offset;
65425cf1a30Sjl 	MC_LOG("pa = %lx\n", *pa);
65525cf1a30Sjl 
65625cf1a30Sjl 	if (pa_to_maddr(mcp, *pa, &maddr1) == -1) {
657*0cc8ae86Sav 		cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to "
658*0cc8ae86Sav 		    "convert PA %lx\n", maddr->ma_bd, bank,
659*0cc8ae86Sav 		    maddr->ma_dimm_addr, *pa);
66025cf1a30Sjl 		return (-1);
66125cf1a30Sjl 	}
66225cf1a30Sjl 
663*0cc8ae86Sav 	/*
664*0cc8ae86Sav 	 * In mirror mode, PA is always translated to the even bank.
665*0cc8ae86Sav 	 */
66625cf1a30Sjl 	if (IS_MIRROR(mcp, maddr->ma_bank)) {
66725cf1a30Sjl 		bank0 = maddr->ma_bank & ~(1);
66825cf1a30Sjl 		bank1 = maddr1.ma_bank & ~(1);
66925cf1a30Sjl 	} else {
67025cf1a30Sjl 		bank0 = maddr->ma_bank;
67125cf1a30Sjl 		bank1 = maddr1.ma_bank;
67225cf1a30Sjl 	}
67325cf1a30Sjl 	/*
67425cf1a30Sjl 	 * there is no need to check ma_bd because it is generated from
67525cf1a30Sjl 	 * mcp.  They are the same.
67625cf1a30Sjl 	 */
67725cf1a30Sjl 	if ((bank0 == bank1) &&
67825cf1a30Sjl 		(maddr->ma_dimm_addr == maddr1.ma_dimm_addr)) {
67925cf1a30Sjl 		return (0);
68025cf1a30Sjl 	} else {
68125cf1a30Sjl 		cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, "
68225cf1a30Sjl 			"PA %lx, target /LSB%d/B%d/%x\n",
68325cf1a30Sjl 			maddr->ma_bd, bank, maddr->ma_dimm_addr,
68425cf1a30Sjl 			*pa, maddr1.ma_bd, maddr1.ma_bank,
68525cf1a30Sjl 			maddr1.ma_dimm_addr);
68625cf1a30Sjl 		return (-1);
68725cf1a30Sjl 	}
68825cf1a30Sjl }
68925cf1a30Sjl 
69025cf1a30Sjl /*
69125cf1a30Sjl  * PA to CS (used by pa_to_maddr).
69225cf1a30Sjl  */
69325cf1a30Sjl static int
69425cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset)
69525cf1a30Sjl {
69625cf1a30Sjl 	int i;
69725cf1a30Sjl 	int cs = 0;
69825cf1a30Sjl 
69925cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
70025cf1a30Sjl 		/* MAC address bit<29> is arranged on the same PA bit */
70125cf1a30Sjl 		/* on both table. So we may use any table. */
70225cf1a30Sjl 		if (mcp->mc_trans_table[0][i] == CS_SHIFT) {
70325cf1a30Sjl 			cs = (pa_offset >> i) & 1;
70425cf1a30Sjl 			break;
70525cf1a30Sjl 		}
70625cf1a30Sjl 	}
70725cf1a30Sjl 	return (cs);
70825cf1a30Sjl }
70925cf1a30Sjl 
71025cf1a30Sjl /*
71125cf1a30Sjl  * PA to DIMM (used by pa_to_maddr).
71225cf1a30Sjl  */
71325cf1a30Sjl /* ARGSUSED */
71425cf1a30Sjl static uint32_t
71525cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset)
71625cf1a30Sjl {
71725cf1a30Sjl 	int i;
71825cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
71925cf1a30Sjl 	uint32_t dimm_addr = 0;
72025cf1a30Sjl 
72125cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
72225cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
72325cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
72425cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
72525cf1a30Sjl 			dimm_addr |= pa_bit_value << mc_bit;
72625cf1a30Sjl 		}
72725cf1a30Sjl 	}
72825cf1a30Sjl 	return (dimm_addr);
72925cf1a30Sjl }
73025cf1a30Sjl 
73125cf1a30Sjl /*
73225cf1a30Sjl  * PA to Bank (used by pa_to_maddr).
73325cf1a30Sjl  */
73425cf1a30Sjl static int
73525cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset)
73625cf1a30Sjl {
73725cf1a30Sjl 	int i;
73825cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
73925cf1a30Sjl 	int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT];
74025cf1a30Sjl 
74125cf1a30Sjl 
74225cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
74325cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
74425cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
74525cf1a30Sjl 		switch (mc_bit) {
74625cf1a30Sjl 		case MP_BANK_0:
74725cf1a30Sjl 			bankno |= pa_bit_value;
74825cf1a30Sjl 			break;
74925cf1a30Sjl 		case MP_BANK_1:
75025cf1a30Sjl 			bankno |= pa_bit_value << 1;
75125cf1a30Sjl 			break;
75225cf1a30Sjl 		case MP_BANK_2:
75325cf1a30Sjl 			bankno |= pa_bit_value << 2;
75425cf1a30Sjl 			break;
75525cf1a30Sjl 		}
75625cf1a30Sjl 	}
75725cf1a30Sjl 
75825cf1a30Sjl 	return (bankno);
75925cf1a30Sjl }
76025cf1a30Sjl 
76125cf1a30Sjl /*
76225cf1a30Sjl  * PA to MAC address translation
76325cf1a30Sjl  *
76425cf1a30Sjl  *   Input: MAC driver state, physicall adress
76525cf1a30Sjl  *   Output: LSB#, Bank id, mac address
76625cf1a30Sjl  *
76725cf1a30Sjl  *    Valid   - return value:  0
76825cf1a30Sjl  *    Invalid - return value: -1
76925cf1a30Sjl  */
77025cf1a30Sjl 
77125cf1a30Sjl int
77225cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr)
77325cf1a30Sjl {
77425cf1a30Sjl 	uint64_t pa_offset;
77525cf1a30Sjl 
77625cf1a30Sjl 	/* PA validity check */
77725cf1a30Sjl 	if (!pa_is_valid(mcp, pa))
77825cf1a30Sjl 		return (-1);
77925cf1a30Sjl 
78025cf1a30Sjl 
78125cf1a30Sjl 	/* Do translation */
78225cf1a30Sjl 	pa_offset = pa - mcp->mc_start_address;
78325cf1a30Sjl 
78425cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
78525cf1a30Sjl 	maddr->ma_bank = pa_to_bank(mcp, pa_offset);
78625cf1a30Sjl 	maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset);
78725cf1a30Sjl 	MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n",
78825cf1a30Sjl 		pa_offset, maddr->ma_bd, maddr->ma_bank, maddr->ma_dimm_addr);
78925cf1a30Sjl 	return (0);
79025cf1a30Sjl }
79125cf1a30Sjl 
792*0cc8ae86Sav /*
793*0cc8ae86Sav  * UNUM format for DC is "/CMUnn/MEMxyZ", where
794*0cc8ae86Sav  *	nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3.
795*0cc8ae86Sav  *	x = MAC 0..3
796*0cc8ae86Sav  *	y = 0..3 (slot info).
797*0cc8ae86Sav  *	Z = 'A' or 'B'
798*0cc8ae86Sav  *
799*0cc8ae86Sav  * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where
800*0cc8ae86Sav  *	x = 0..3 (MEMB number)
801*0cc8ae86Sav  *	y = 0..3 (slot info).
802*0cc8ae86Sav  *	Z = 'A' or 'B'
803*0cc8ae86Sav  *
804*0cc8ae86Sav  * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ"
805*0cc8ae86Sav  *	x = 0..7 (MEMB number)
806*0cc8ae86Sav  *	y = 0..3 (slot info).
807*0cc8ae86Sav  *	Z = 'A' or 'B'
808*0cc8ae86Sav  */
809*0cc8ae86Sav int
810*0cc8ae86Sav mc_set_mem_unum(char *buf, int buflen, int lsb, int bank,
811*0cc8ae86Sav     uint32_t mf_type, uint32_t d_slot)
812*0cc8ae86Sav {
813*0cc8ae86Sav 	char *dimmnm;
814*0cc8ae86Sav 	char memb_num;
815*0cc8ae86Sav 	int sb;
816*0cc8ae86Sav 	int i;
817*0cc8ae86Sav 
818*0cc8ae86Sav 	if ((sb = mc_opl_get_physical_board(lsb)) < 0)
819*0cc8ae86Sav 		return (ENODEV);
820*0cc8ae86Sav 
821*0cc8ae86Sav 	if (plat_model == MODEL_DC) {
822*0cc8ae86Sav 		if (mf_type == FLT_TYPE_PERMANENT_CE) {
823*0cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
824*0cc8ae86Sav 			dimmnm = mc_dc_dimm_unum_table[i];
825*0cc8ae86Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s",
826*0cc8ae86Sav 			    model_names[plat_model].unit_name, sb, dimmnm);
827*0cc8ae86Sav 		} else {
828*0cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
829*0cc8ae86Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s MEM%s MEM%s",
830*0cc8ae86Sav 			    model_names[plat_model].unit_name, sb,
831*0cc8ae86Sav 			    mc_dc_dimm_unum_table[i],
832*0cc8ae86Sav 			    mc_dc_dimm_unum_table[i + 1],
833*0cc8ae86Sav 			    mc_dc_dimm_unum_table[i + 2],
834*0cc8ae86Sav 			    mc_dc_dimm_unum_table[i + 3]);
835*0cc8ae86Sav 		}
836*0cc8ae86Sav 	} else {
837*0cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
838*0cc8ae86Sav 		if (mf_type == FLT_TYPE_PERMANENT_CE) {
839*0cc8ae86Sav 			dimmnm = mc_ff_dimm_unum_table[i];
840*0cc8ae86Sav 			memb_num = dimmnm[0];
841*0cc8ae86Sav 			snprintf(buf, buflen, "/%s/%s%c/MEM%s",
842*0cc8ae86Sav 			    model_names[plat_model].unit_name,
843*0cc8ae86Sav 			    model_names[plat_model].mem_name,
844*0cc8ae86Sav 			    memb_num, &dimmnm[1]);
845*0cc8ae86Sav 		} else {
846*0cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
847*0cc8ae86Sav 			memb_num = mc_ff_dimm_unum_table[i][0],
848*0cc8ae86Sav 			snprintf(buf, buflen,
849*0cc8ae86Sav 			    "/%s/%s%c/MEM%s MEM%s MEM%s MEM%s",
850*0cc8ae86Sav 			    model_names[plat_model].unit_name,
851*0cc8ae86Sav 			    model_names[plat_model].mem_name, memb_num,
852*0cc8ae86Sav 			    &mc_ff_dimm_unum_table[i][1],
853*0cc8ae86Sav 			    &mc_ff_dimm_unum_table[i + 1][1],
854*0cc8ae86Sav 			    &mc_ff_dimm_unum_table[i + 2][1],
855*0cc8ae86Sav 			    &mc_ff_dimm_unum_table[i + 3][1]);
856*0cc8ae86Sav 		}
857*0cc8ae86Sav 	}
858*0cc8ae86Sav 	return (0);
859*0cc8ae86Sav }
860*0cc8ae86Sav 
86125cf1a30Sjl static void
86225cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt)
86325cf1a30Sjl {
86425cf1a30Sjl 	char buf[FM_MAX_CLASS];
86525cf1a30Sjl 	char device_path[MAXPATHLEN];
866*0cc8ae86Sav 	char sid[MAXPATHLEN];
86725cf1a30Sjl 	nv_alloc_t *nva = NULL;
86825cf1a30Sjl 	nvlist_t *ereport, *detector, *resource;
86925cf1a30Sjl 	errorq_elem_t *eqep;
87025cf1a30Sjl 	int nflts;
87125cf1a30Sjl 	mc_flt_stat_t *flt_stat;
872*0cc8ae86Sav 	int i, n;
873*0cc8ae86Sav 	int blen = MAXPATHLEN;
874*0cc8ae86Sav 	char *p, *s = NULL;
87525cf1a30Sjl 	uint32_t values[2], synd[2], dslot[2];
876*0cc8ae86Sav 	uint64_t offset = (uint64_t)-1;
877*0cc8ae86Sav 	int ret = -1;
87825cf1a30Sjl 
87925cf1a30Sjl 	if (panicstr) {
88025cf1a30Sjl 		eqep = errorq_reserve(ereport_errorq);
88125cf1a30Sjl 		if (eqep == NULL)
88225cf1a30Sjl 			return;
88325cf1a30Sjl 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
88425cf1a30Sjl 		nva = errorq_elem_nva(ereport_errorq, eqep);
88525cf1a30Sjl 	} else {
88625cf1a30Sjl 		ereport = fm_nvlist_create(nva);
88725cf1a30Sjl 	}
88825cf1a30Sjl 
88925cf1a30Sjl 	/*
89025cf1a30Sjl 	 * Create the scheme "dev" FMRI.
89125cf1a30Sjl 	 */
89225cf1a30Sjl 	detector = fm_nvlist_create(nva);
89325cf1a30Sjl 	resource = fm_nvlist_create(nva);
89425cf1a30Sjl 
89525cf1a30Sjl 	nflts = mc_aflt->mflt_nflts;
89625cf1a30Sjl 
89725cf1a30Sjl 	ASSERT(nflts >= 1 && nflts <= 2);
89825cf1a30Sjl 
89925cf1a30Sjl 	flt_stat = mc_aflt->mflt_stat[0];
90025cf1a30Sjl 	(void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path);
90125cf1a30Sjl 	(void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
90225cf1a30Sjl 	    device_path, NULL);
90325cf1a30Sjl 
90425cf1a30Sjl 	/*
90525cf1a30Sjl 	 * Encode all the common data into the ereport.
90625cf1a30Sjl 	 */
90725cf1a30Sjl 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s",
90825cf1a30Sjl 		MC_OPL_ERROR_CLASS,
90925cf1a30Sjl 		mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS :
91025cf1a30Sjl 		MC_OPL_MI_SUBCLASS,
91125cf1a30Sjl 		mc_aflt->mflt_erpt_class);
91225cf1a30Sjl 
91325cf1a30Sjl 	MC_LOG("mc_ereport_post: ereport %s\n", buf);
91425cf1a30Sjl 
91525cf1a30Sjl 
91625cf1a30Sjl 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
91725cf1a30Sjl 		fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1),
91825cf1a30Sjl 		detector, NULL);
91925cf1a30Sjl 
92025cf1a30Sjl 	/*
92125cf1a30Sjl 	 * Set payload.
92225cf1a30Sjl 	 */
92325cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32,
92425cf1a30Sjl 		flt_stat->mf_flt_maddr.ma_bd, NULL);
92525cf1a30Sjl 
92625cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64,
92725cf1a30Sjl 		flt_stat->mf_flt_paddr, NULL);
92825cf1a30Sjl 
92925cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
93025cf1a30Sjl 		fm_payload_set(ereport, MC_OPL_FLT_TYPE,
93125cf1a30Sjl 			DATA_TYPE_UINT8, ECC_STICKY, NULL);
93225cf1a30Sjl 	}
93325cf1a30Sjl 
93425cf1a30Sjl 	for (i = 0; i < nflts; i++)
93525cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank;
93625cf1a30Sjl 
93725cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY,
93825cf1a30Sjl 		nflts, values, NULL);
93925cf1a30Sjl 
94025cf1a30Sjl 	for (i = 0; i < nflts; i++)
94125cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_cntl;
94225cf1a30Sjl 
94325cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY,
94425cf1a30Sjl 		nflts, values, NULL);
94525cf1a30Sjl 
94625cf1a30Sjl 	for (i = 0; i < nflts; i++)
94725cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_add;
94825cf1a30Sjl 
949*0cc8ae86Sav 	/* offset is set only for PCE */
950*0cc8ae86Sav 	if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) {
951*0cc8ae86Sav 		offset = values[0];
952*0cc8ae86Sav 
953*0cc8ae86Sav 	}
95425cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY,
95525cf1a30Sjl 		nflts, values, NULL);
95625cf1a30Sjl 
95725cf1a30Sjl 	for (i = 0; i < nflts; i++)
95825cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_log;
95925cf1a30Sjl 
96025cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY,
96125cf1a30Sjl 		nflts, values, NULL);
96225cf1a30Sjl 
96325cf1a30Sjl 	for (i = 0; i < nflts; i++) {
96425cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
96525cf1a30Sjl 		if (flt_stat->mf_errlog_valid) {
96625cf1a30Sjl 			synd[i] = flt_stat->mf_synd;
96725cf1a30Sjl 			dslot[i] = flt_stat->mf_dimm_slot;
96825cf1a30Sjl 			values[i] = flt_stat->mf_dram_place;
96925cf1a30Sjl 		} else {
97025cf1a30Sjl 			synd[i] = 0;
97125cf1a30Sjl 			dslot[i] = 0;
97225cf1a30Sjl 			values[i] = 0;
97325cf1a30Sjl 		}
97425cf1a30Sjl 	}
97525cf1a30Sjl 
97625cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_SYND,
97725cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, synd, NULL);
97825cf1a30Sjl 
97925cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT,
98025cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, dslot, NULL);
98125cf1a30Sjl 
98225cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_DRAM,
98325cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, values, NULL);
98425cf1a30Sjl 
98525cf1a30Sjl 	device_path[0] = 0;
98625cf1a30Sjl 	p = &device_path[0];
987*0cc8ae86Sav 	sid[0] = 0;
988*0cc8ae86Sav 	s = &sid[0];
989*0cc8ae86Sav 	ret = 0;
99025cf1a30Sjl 
99125cf1a30Sjl 	for (i = 0; i < nflts; i++) {
992*0cc8ae86Sav 		int bank;
99325cf1a30Sjl 
99425cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
995*0cc8ae86Sav 		bank = flt_stat->mf_flt_maddr.ma_bank;
996*0cc8ae86Sav 		ret =  mc_set_mem_unum(p + strlen(p), blen,
997*0cc8ae86Sav 			flt_stat->mf_flt_maddr.ma_bd, bank, flt_stat->mf_type,
998*0cc8ae86Sav 			flt_stat->mf_dimm_slot);
999*0cc8ae86Sav 
1000*0cc8ae86Sav 		if (ret != 0) {
1001*0cc8ae86Sav 			cmn_err(CE_WARN,
1002*0cc8ae86Sav 			    "mc_ereport_post: Failed to determine the unum "
1003*0cc8ae86Sav 			    "for board=%d bank=%d type=0x%x slot=0x%x",
1004*0cc8ae86Sav 			    flt_stat->mf_flt_maddr.ma_bd, bank,
1005*0cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
1006*0cc8ae86Sav 			continue;
100725cf1a30Sjl 		}
1008*0cc8ae86Sav 		n = strlen(device_path);
100925cf1a30Sjl 		blen = MAXPATHLEN - n;
101025cf1a30Sjl 		p = &device_path[n];
101125cf1a30Sjl 		if (i < (nflts - 1)) {
101225cf1a30Sjl 			snprintf(p, blen, " ");
1013*0cc8ae86Sav 			blen--;
1014*0cc8ae86Sav 			p++;
1015*0cc8ae86Sav 		}
1016*0cc8ae86Sav 
1017*0cc8ae86Sav 		if (ret == 0) {
1018*0cc8ae86Sav 			ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s),
1019*0cc8ae86Sav 			    blen, flt_stat->mf_flt_maddr.ma_bd, bank,
1020*0cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
1021*0cc8ae86Sav 
102225cf1a30Sjl 		}
102325cf1a30Sjl 	}
102425cf1a30Sjl 
102525cf1a30Sjl 	(void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION,
1026*0cc8ae86Sav 		NULL, device_path, (ret == 0) ? sid : NULL,
1027*0cc8ae86Sav 		(ret == 0) ? offset : (uint64_t)-1);
102825cf1a30Sjl 
102925cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST,
103025cf1a30Sjl 		resource, NULL);
103125cf1a30Sjl 
103225cf1a30Sjl 	if (panicstr) {
103325cf1a30Sjl 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
103425cf1a30Sjl 	} else {
103525cf1a30Sjl 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
103625cf1a30Sjl 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
103725cf1a30Sjl 		fm_nvlist_destroy(detector, FM_NVA_FREE);
103825cf1a30Sjl 		fm_nvlist_destroy(resource, FM_NVA_FREE);
103925cf1a30Sjl 	}
104025cf1a30Sjl }
104125cf1a30Sjl 
1042*0cc8ae86Sav 
104325cf1a30Sjl static void
104425cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt)
104525cf1a30Sjl {
104625cf1a30Sjl 	int rv;
104725cf1a30Sjl 	page_t *pp;
104825cf1a30Sjl 	uint64_t errors;
104925cf1a30Sjl 	uint64_t pa = (uint64_t)(-1);
1050*0cc8ae86Sav 	int i;
105125cf1a30Sjl 
105225cf1a30Sjl 	MC_LOG("mc_err_drain: %s\n",
105325cf1a30Sjl 		mc_aflt->mflt_erpt_class);
105425cf1a30Sjl 	/*
105525cf1a30Sjl 	 * we come here only when we have:
105625cf1a30Sjl 	 * In mirror mode: CMPE, MUE, SUE
105725cf1a30Sjl 	 * In normal mode: UE, Permanent CE
105825cf1a30Sjl 	 */
1059*0cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
1060*0cc8ae86Sav 		rv = mcaddr_to_pa(mc_aflt->mflt_mcp,
1061*0cc8ae86Sav 			&(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa);
1062*0cc8ae86Sav 		if (rv == 0)
1063*0cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = pa;
1064*0cc8ae86Sav 		else
1065*0cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1;
1066*0cc8ae86Sav 	}
1067*0cc8ae86Sav 
1068*0cc8ae86Sav 	if (mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) {
106925cf1a30Sjl 		MC_LOG("mc_err_drain:pa = %lx\n", pa);
107025cf1a30Sjl 		pp = page_numtopp_nolock(pa >> PAGESHIFT);
107125cf1a30Sjl 
107225cf1a30Sjl 		if (pp) {
107325cf1a30Sjl 			/*
107425cf1a30Sjl 			 * Don't keep retiring and make ereports
107525cf1a30Sjl 			 * on bad pages in PTRL case
107625cf1a30Sjl 			 */
107725cf1a30Sjl 			MC_LOG("mc_err_drain:pp = %p\n", pp);
107825cf1a30Sjl 			if (mc_aflt->mflt_is_ptrl) {
107925cf1a30Sjl 				errors = 0;
108025cf1a30Sjl 				if (page_retire_check(pa, &errors) == 0) {
108125cf1a30Sjl 					MC_LOG("Page retired\n");
108225cf1a30Sjl 					return;
108325cf1a30Sjl 				}
108425cf1a30Sjl 				if (errors & mc_aflt->mflt_pr) {
108525cf1a30Sjl 					MC_LOG("errors %lx, mflt_pr %x\n",
108625cf1a30Sjl 						errors, mc_aflt->mflt_pr);
108725cf1a30Sjl 					return;
108825cf1a30Sjl 				}
108925cf1a30Sjl 			}
109025cf1a30Sjl 			MC_LOG("offline page %p error %x\n", pp,
109125cf1a30Sjl 				mc_aflt->mflt_pr);
109225cf1a30Sjl 			(void) page_retire(pa, mc_aflt->mflt_pr);
109325cf1a30Sjl 		}
109425cf1a30Sjl 	}
109525cf1a30Sjl 
1096*0cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
1097*0cc8ae86Sav 		mc_aflt_t mc_aflt0;
1098*0cc8ae86Sav 		if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) {
1099*0cc8ae86Sav 			mc_aflt0 = *mc_aflt;
1100*0cc8ae86Sav 			mc_aflt0.mflt_nflts = 1;
1101*0cc8ae86Sav 			mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i];
1102*0cc8ae86Sav 			mc_ereport_post(&mc_aflt0);
1103*0cc8ae86Sav 		}
1104*0cc8ae86Sav 	}
1105*0cc8ae86Sav }
110625cf1a30Sjl 
110725cf1a30Sjl /*
110825cf1a30Sjl  * The restart address is actually defined in unit of PA[37:6]
110925cf1a30Sjl  * the mac patrol will convert that to dimm offset.  If the
111025cf1a30Sjl  * address is not in the bank, it will continue to search for
111125cf1a30Sjl  * the next PA that is within the bank.
111225cf1a30Sjl  *
111325cf1a30Sjl  * Also the mac patrol scans the dimms based on PA, not
111425cf1a30Sjl  * dimm offset.
111525cf1a30Sjl  */
111625cf1a30Sjl static int
111725cf1a30Sjl restart_patrol(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr_info)
111825cf1a30Sjl {
111925cf1a30Sjl 	page_t *pp;
112025cf1a30Sjl 	uint64_t pa;
112125cf1a30Sjl 	int rv;
112225cf1a30Sjl 	int loop_count = 0;
112325cf1a30Sjl 
112425cf1a30Sjl 	if (maddr_info == NULL || (maddr_info->mi_valid == 0)) {
112525cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
112625cf1a30Sjl 		return (0);
112725cf1a30Sjl 	}
112825cf1a30Sjl 
112925cf1a30Sjl 	rv = mcaddr_to_pa(mcp, &maddr_info->mi_maddr, &pa);
113025cf1a30Sjl 	if (rv != 0) {
113125cf1a30Sjl 		MC_LOG("cannot convert mcaddr to pa. use auto restart\n");
113225cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
113325cf1a30Sjl 		return (0);
113425cf1a30Sjl 	}
113525cf1a30Sjl 
113625cf1a30Sjl 	/*
113725cf1a30Sjl 	 * pa is the last address scanned by the mac patrol
113825cf1a30Sjl 	 * we  calculate the next restart address as follows:
113925cf1a30Sjl 	 * first we always advance it by 64 byte. Then begin the loop.
114025cf1a30Sjl 	 * loop {
114125cf1a30Sjl 	 * if it is not in phys_install, we advance to next 64 MB boundary
114225cf1a30Sjl 	 * if it is not backed by a page structure, done
114325cf1a30Sjl 	 * if the page is bad, advance to the next page boundary.
114425cf1a30Sjl 	 * else done
114525cf1a30Sjl 	 * if the new address exceeds the board, wrap around.
114625cf1a30Sjl 	 * } <stop if we come back to the same page>
114725cf1a30Sjl 	 */
114825cf1a30Sjl 
114925cf1a30Sjl 	if (pa < mcp->mc_start_address || pa >= (mcp->mc_start_address
115025cf1a30Sjl 		+ mcp->mc_size)) {
115125cf1a30Sjl 		/* pa is not on this board, just retry */
115225cf1a30Sjl 		cmn_err(CE_WARN, "restart_patrol: invalid address %lx "
115325cf1a30Sjl 			"on board %d\n", pa, mcp->mc_board_num);
115425cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
115525cf1a30Sjl 		return (0);
115625cf1a30Sjl 	}
115725cf1a30Sjl 
115825cf1a30Sjl 	MC_LOG("restart_patrol: pa = %lx\n", pa);
115925cf1a30Sjl 	if (maddr_info->mi_advance) {
116025cf1a30Sjl 		uint64_t new_pa;
116125cf1a30Sjl 
116225cf1a30Sjl 		if (IS_MIRROR(mcp, bank))
116325cf1a30Sjl 			new_pa = pa + 64 * 2;
116425cf1a30Sjl 		else
116525cf1a30Sjl 			new_pa = pa + 64;
116625cf1a30Sjl 
116725cf1a30Sjl 		if (!mc_valid_pa(mcp, new_pa)) {
116825cf1a30Sjl 			MC_LOG("Invalid PA\n");
1169*0cc8ae86Sav 			pa = roundup(new_pa + 1, mc_isolation_bsize);
117025cf1a30Sjl 		} else {
117125cf1a30Sjl 			pp = page_numtopp_nolock(new_pa >> PAGESHIFT);
117225cf1a30Sjl 			if (pp != NULL) {
117325cf1a30Sjl 				uint64_t errors = 0;
117425cf1a30Sjl 				if (page_retire_check(new_pa, &errors) &&
117525cf1a30Sjl 					(errors == 0)) {
117625cf1a30Sjl 					MC_LOG("Page has no error\n");
1177*0cc8ae86Sav 					pa = new_pa;
1178*0cc8ae86Sav 					goto done;
117925cf1a30Sjl 				}
118025cf1a30Sjl 				/*
118125cf1a30Sjl 				 * skip bad pages
118225cf1a30Sjl 				 * and let the following loop to take care
118325cf1a30Sjl 				 */
118425cf1a30Sjl 				pa = roundup(new_pa + 1, PAGESIZE);
118525cf1a30Sjl 				MC_LOG("Skipping bad page to %lx\n", pa);
118625cf1a30Sjl 			} else {
118725cf1a30Sjl 				MC_LOG("Page has no page structure\n");
1188*0cc8ae86Sav 				pa = new_pa;
1189*0cc8ae86Sav 				goto done;
119025cf1a30Sjl 			}
119125cf1a30Sjl 		}
119225cf1a30Sjl 	}
119325cf1a30Sjl 
119425cf1a30Sjl 	/*
119525cf1a30Sjl 	 * if we wrap around twice, we just give up and let
119625cf1a30Sjl 	 * mac patrol decide.
119725cf1a30Sjl 	 */
119825cf1a30Sjl 	MC_LOG("pa is now %lx\n", pa);
119925cf1a30Sjl 	while (loop_count <= 1) {
120025cf1a30Sjl 		if (!mc_valid_pa(mcp, pa)) {
120125cf1a30Sjl 			MC_LOG("pa is not valid. round up to 64 MB\n");
120225cf1a30Sjl 			pa = roundup(pa + 1, 64 * 1024 * 1024);
120325cf1a30Sjl 		} else {
120425cf1a30Sjl 			pp = page_numtopp_nolock(pa >> PAGESHIFT);
120525cf1a30Sjl 			if (pp != NULL) {
120625cf1a30Sjl 				uint64_t errors = 0;
120725cf1a30Sjl 				if (page_retire_check(pa, &errors) &&
120825cf1a30Sjl 					(errors == 0)) {
120925cf1a30Sjl 					MC_LOG("Page has no error\n");
121025cf1a30Sjl 					break;
121125cf1a30Sjl 				}
121225cf1a30Sjl 				/* skip bad pages */
121325cf1a30Sjl 				pa = roundup(pa + 1, PAGESIZE);
121425cf1a30Sjl 				MC_LOG("Skipping bad page to %lx\n", pa);
121525cf1a30Sjl 			} else {
121625cf1a30Sjl 				MC_LOG("Page has no page structure\n");
121725cf1a30Sjl 				break;
121825cf1a30Sjl 			}
121925cf1a30Sjl 		}
122025cf1a30Sjl 		if (pa >= (mcp->mc_start_address + mcp->mc_size)) {
122125cf1a30Sjl 			MC_LOG("Wrap around\n");
122225cf1a30Sjl 			pa = mcp->mc_start_address;
122325cf1a30Sjl 			loop_count++;
122425cf1a30Sjl 		}
122525cf1a30Sjl 	}
122625cf1a30Sjl 
1227*0cc8ae86Sav done:
122825cf1a30Sjl 	/* retstart MAC patrol: PA[37:6] */
122925cf1a30Sjl 	MC_LOG("restart at pa = %lx\n", pa);
123025cf1a30Sjl 	ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa));
123125cf1a30Sjl 	MAC_PTRL_START_ADD(mcp, bank);
123225cf1a30Sjl 
123325cf1a30Sjl 	return (0);
123425cf1a30Sjl }
123525cf1a30Sjl 
123625cf1a30Sjl /*
123725cf1a30Sjl  * Rewriting is used for two purposes.
123825cf1a30Sjl  *  - to correct the error in memory.
123925cf1a30Sjl  *  - to determine whether the error is permanent or intermittent.
124025cf1a30Sjl  * It's done by writing the address in MAC_BANKm_REWRITE_ADD
124125cf1a30Sjl  * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that,
124225cf1a30Sjl  * REW_END (and REW_CE/REW_UE if some error detected) is set when
124325cf1a30Sjl  * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM.
124425cf1a30Sjl  *
124525cf1a30Sjl  * Note that rewrite operation doesn't change RAW_UE to Marked UE.
124625cf1a30Sjl  * Therefore, we use it only CE case.
124725cf1a30Sjl  */
124825cf1a30Sjl static uint32_t
124925cf1a30Sjl do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr)
125025cf1a30Sjl {
125125cf1a30Sjl 	uint32_t cntl;
125225cf1a30Sjl 	int count = 0;
125325cf1a30Sjl 
125425cf1a30Sjl 	/* first wait to make sure PTRL_STATUS is 0 */
1255*0cc8ae86Sav 	while (count++ < mc_max_rewrite_loop) {
125625cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
125725cf1a30Sjl 		if (!(cntl & MAC_CNTL_PTRL_STATUS))
125825cf1a30Sjl 			break;
1259*0cc8ae86Sav 		drv_usecwait(mc_rewrite_delay);
126025cf1a30Sjl 	}
1261*0cc8ae86Sav 	if (count >= mc_max_rewrite_loop)
126225cf1a30Sjl 		goto bad;
126325cf1a30Sjl 
126425cf1a30Sjl 	count = 0;
126525cf1a30Sjl 
126625cf1a30Sjl 	ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr);
126725cf1a30Sjl 	MAC_REW_REQ(mcp, bank);
126825cf1a30Sjl 
126925cf1a30Sjl 	do {
127025cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
1271*0cc8ae86Sav 		if (count++ >= mc_max_rewrite_loop) {
127225cf1a30Sjl 			goto bad;
1273*0cc8ae86Sav 		} else {
1274*0cc8ae86Sav 			drv_usecwait(mc_rewrite_delay);
1275*0cc8ae86Sav 		}
127625cf1a30Sjl 	/*
127725cf1a30Sjl 	 * If there are other MEMORY or PCI activities, this
127825cf1a30Sjl 	 * will be BUSY, else it should be set immediately
127925cf1a30Sjl 	 */
128025cf1a30Sjl 	} while (!(cntl & MAC_CNTL_REW_END));
128125cf1a30Sjl 
128225cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
128325cf1a30Sjl 	return (cntl);
128425cf1a30Sjl bad:
128525cf1a30Sjl 	/* This is bad.  Just reset the circuit */
128625cf1a30Sjl 	cmn_err(CE_WARN, "mc-opl rewrite timeout on /LSB%d/B%d\n",
128725cf1a30Sjl 		mcp->mc_board_num, bank);
128825cf1a30Sjl 	cntl = MAC_CNTL_REW_END;
128925cf1a30Sjl 	MAC_CMD(mcp, bank, MAC_CNTL_PTRL_RESET);
129025cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
129125cf1a30Sjl 	return (cntl);
129225cf1a30Sjl }
129325cf1a30Sjl void
129425cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp)
129525cf1a30Sjl {
1296*0cc8ae86Sav 	int count;
1297*0cc8ae86Sav 	int n = 0;
129825cf1a30Sjl 	scf_log_t *p;
129925cf1a30Sjl 	int bank;
130025cf1a30Sjl 
1301*0cc8ae86Sav 	for (bank = 0; bank < BANKNUM_PER_SB; bank++) {
1302*0cc8ae86Sav 	    while ((p = mcp->mc_scf_log[bank]) != NULL &&
1303*0cc8ae86Sav 		(n < mc_max_errlog_processed)) {
1304*0cc8ae86Sav 		ASSERT(bank == p->sl_bank);
1305*0cc8ae86Sav 		count = 0;
130625cf1a30Sjl 		while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank))
130725cf1a30Sjl 			& MAC_STATIC_ERR_VLD)) {
1308*0cc8ae86Sav 			if (count++ >= (mc_max_scf_loop)) {
130925cf1a30Sjl 				break;
131025cf1a30Sjl 			}
1311*0cc8ae86Sav 			drv_usecwait(mc_scf_delay);
131225cf1a30Sjl 		}
131325cf1a30Sjl 
1314*0cc8ae86Sav 		if (count < mc_max_scf_loop) {
131525cf1a30Sjl 			ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank),
131625cf1a30Sjl 				p->sl_err_log);
131725cf1a30Sjl 
131825cf1a30Sjl 			ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank),
131925cf1a30Sjl 				p->sl_err_add|MAC_STATIC_ERR_VLD);
132025cf1a30Sjl 			mcp->mc_scf_retry[bank] = 0;
132125cf1a30Sjl 		} else {
132225cf1a30Sjl 			/* if we try too many times, just drop the req */
1323*0cc8ae86Sav 			if (mcp->mc_scf_retry[bank]++ <= mc_max_scf_retry) {
132425cf1a30Sjl 				return;
132525cf1a30Sjl 			} else {
1326*0cc8ae86Sav 			    if ((++mc_pce_dropped & 0xff) == 0) {
1327*0cc8ae86Sav 				cmn_err(CE_WARN,
1328*0cc8ae86Sav 				    "Cannot report Permanent CE to SCF\n");
1329*0cc8ae86Sav 			    }
133025cf1a30Sjl 			}
133125cf1a30Sjl 		}
1332*0cc8ae86Sav 		n++;
1333*0cc8ae86Sav 		mcp->mc_scf_log[bank] = p->sl_next;
1334*0cc8ae86Sav 		mcp->mc_scf_total[bank]--;
1335*0cc8ae86Sav 		ASSERT(mcp->mc_scf_total[bank] >= 0);
133625cf1a30Sjl 		kmem_free(p, sizeof (scf_log_t));
1337*0cc8ae86Sav 	    }
133825cf1a30Sjl 	}
133925cf1a30Sjl }
134025cf1a30Sjl void
134125cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank)
134225cf1a30Sjl {
134325cf1a30Sjl 	scf_log_t *p;
134425cf1a30Sjl 
1345*0cc8ae86Sav 	if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) {
1346*0cc8ae86Sav 		if ((++mc_pce_dropped & 0xff) == 0) {
1347*0cc8ae86Sav 		    cmn_err(CE_WARN, "Too many Permanent CE requests.\n");
1348*0cc8ae86Sav 		}
134925cf1a30Sjl 		return;
135025cf1a30Sjl 	}
135125cf1a30Sjl 	p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP);
135225cf1a30Sjl 	p->sl_next = 0;
135325cf1a30Sjl 	p->sl_err_add = flt_stat->mf_err_add;
135425cf1a30Sjl 	p->sl_err_log = flt_stat->mf_err_log;
135525cf1a30Sjl 	p->sl_bank = bank;
135625cf1a30Sjl 
1357*0cc8ae86Sav 	if (mcp->mc_scf_log[bank] == NULL) {
135825cf1a30Sjl 		/*
135925cf1a30Sjl 		 * we rely on mc_scf_log to detect NULL queue.
136025cf1a30Sjl 		 * mc_scf_log_tail is irrelevant is such case.
136125cf1a30Sjl 		 */
1362*0cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p;
136325cf1a30Sjl 	} else {
1364*0cc8ae86Sav 		mcp->mc_scf_log_tail[bank]->sl_next = p;
1365*0cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = p;
136625cf1a30Sjl 	}
1367*0cc8ae86Sav 	mcp->mc_scf_total[bank]++;
136825cf1a30Sjl }
136925cf1a30Sjl /*
137025cf1a30Sjl  * This routine determines what kind of CE happens, intermittent
137125cf1a30Sjl  * or permanent as follows. (See 4.7.3 in Columbus2 PRM.)
137225cf1a30Sjl  * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register.
137325cf1a30Sjl  * - If CE is still detected on the same address even after doing
137425cf1a30Sjl  *   rewrite operation twice, it is determined as permanent error.
137525cf1a30Sjl  * - If error is not detected anymore, it is determined as intermittent
137625cf1a30Sjl  *   error.
137725cf1a30Sjl  * - If UE is detected due to rewrite operation, it should be treated
137825cf1a30Sjl  *   as UE.
137925cf1a30Sjl  */
138025cf1a30Sjl 
138125cf1a30Sjl /* ARGSUSED */
138225cf1a30Sjl static void
138325cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error)
138425cf1a30Sjl {
138525cf1a30Sjl 	uint32_t cntl;
138625cf1a30Sjl 	int i;
138725cf1a30Sjl 
138825cf1a30Sjl 	flt_stat->mf_type = FLT_TYPE_PERMANENT_CE;
138925cf1a30Sjl 	/*
139025cf1a30Sjl 	 * rewrite request 1st time reads and correct error data
139125cf1a30Sjl 	 * and write to DIMM.  2nd rewrite request must be issued
139225cf1a30Sjl 	 * after REW_CE/UE/END is 0.  When the 2nd request is completed,
139325cf1a30Sjl 	 * if REW_CE = 1, then it is permanent CE.
139425cf1a30Sjl 	 */
139525cf1a30Sjl 	for (i = 0; i < 2; i++) {
139625cf1a30Sjl 		cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add);
139725cf1a30Sjl 		/*
139825cf1a30Sjl 		 * If the error becomes UE or CMPE
139925cf1a30Sjl 		 * we return to the caller immediately.
140025cf1a30Sjl 		 */
140125cf1a30Sjl 		if (cntl & MAC_CNTL_REW_UE) {
140225cf1a30Sjl 			if (ptrl_error)
140325cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE;
140425cf1a30Sjl 			else
140525cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_UE;
140625cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_UE;
140725cf1a30Sjl 			return;
140825cf1a30Sjl 		}
140925cf1a30Sjl 		if (cntl & MAC_CNTL_REW_CMPE) {
141025cf1a30Sjl 			if (ptrl_error)
141125cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE;
141225cf1a30Sjl 			else
141325cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE;
141425cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_CMPE;
141525cf1a30Sjl 			return;
141625cf1a30Sjl 		}
141725cf1a30Sjl 	}
141825cf1a30Sjl 	if (!(cntl & MAC_CNTL_REW_CE)) {
141925cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE;
142025cf1a30Sjl 	}
142125cf1a30Sjl 
142225cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
142325cf1a30Sjl 		/* report PERMANENT_CE to SP via SCF */
142425cf1a30Sjl 		if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) {
142525cf1a30Sjl 			mc_queue_scf_log(mcp, flt_stat, bank);
142625cf1a30Sjl 		}
142725cf1a30Sjl 	}
142825cf1a30Sjl }
142925cf1a30Sjl 
143025cf1a30Sjl #define	IS_CMPE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\
143125cf1a30Sjl 				MAC_CNTL_MI_CMPE))
143225cf1a30Sjl #define	IS_UE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE))
143325cf1a30Sjl #define	IS_CE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE))
143425cf1a30Sjl #define	IS_OK(cntl, f)	(!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \
143525cf1a30Sjl 			MAC_CNTL_MI_ERRS)))
143625cf1a30Sjl 
143725cf1a30Sjl 
143825cf1a30Sjl static int
143925cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error)
144025cf1a30Sjl {
144125cf1a30Sjl 	if (ptrl_error) {
144225cf1a30Sjl 		return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE);
144325cf1a30Sjl 	} else {
144425cf1a30Sjl 		return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE);
144525cf1a30Sjl 	}
144625cf1a30Sjl }
144725cf1a30Sjl 
144825cf1a30Sjl void
144925cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value)
145025cf1a30Sjl {
1451*0cc8ae86Sav 	if (mcp->mc_speedup_period[bank] > 0)
1452*0cc8ae86Sav 		value |= mc_max_speed;
1453*0cc8ae86Sav 	else
1454*0cc8ae86Sav 		value |= mcp->mc_speed;
145525cf1a30Sjl 	ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value);
145625cf1a30Sjl }
145725cf1a30Sjl 
145825cf1a30Sjl static void
145925cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
146025cf1a30Sjl {
146125cf1a30Sjl 	flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
146225cf1a30Sjl 		MAC_CNTL_PTRL_ERRS;
146325cf1a30Sjl 	flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank));
146425cf1a30Sjl 	flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank));
146525cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
146625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
146725cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
146825cf1a30Sjl }
146925cf1a30Sjl 
147025cf1a30Sjl static void
147125cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
147225cf1a30Sjl {
147325cf1a30Sjl 	uint32_t status, old_status;
147425cf1a30Sjl 
147525cf1a30Sjl 	status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
147625cf1a30Sjl 		MAC_CNTL_MI_ERRS;
147725cf1a30Sjl 	old_status = 0;
147825cf1a30Sjl 
147925cf1a30Sjl 	/* we keep reading until the status is stable */
148025cf1a30Sjl 	while (old_status != status) {
148125cf1a30Sjl 		old_status = status;
148225cf1a30Sjl 		flt_stat->mf_err_add =
148325cf1a30Sjl 			LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank));
148425cf1a30Sjl 		flt_stat->mf_err_log =
148525cf1a30Sjl 			LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank));
148625cf1a30Sjl 		status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
148725cf1a30Sjl 			MAC_CNTL_MI_ERRS;
148825cf1a30Sjl 		if (status == old_status) {
148925cf1a30Sjl 			break;
149025cf1a30Sjl 		}
149125cf1a30Sjl 	}
149225cf1a30Sjl 
149325cf1a30Sjl 	flt_stat->mf_cntl = status;
149425cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
149525cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
149625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
149725cf1a30Sjl }
149825cf1a30Sjl 
149925cf1a30Sjl 
150025cf1a30Sjl /*
150125cf1a30Sjl  * Error philosophy for mirror mode:
150225cf1a30Sjl  *
150325cf1a30Sjl  * PTRL (The error address for both banks are same, since ptrl stops if it
150425cf1a30Sjl  * detects error.)
150525cf1a30Sjl  * - Compaire error  Report CMPE.
150625cf1a30Sjl  *
150725cf1a30Sjl  * - UE-UE           Report MUE.  No rewrite.
150825cf1a30Sjl  *
150925cf1a30Sjl  * - UE-*	     UE-(CE/OK). Rewrite to scrub UE.  Report SUE.
151025cf1a30Sjl  *
151125cf1a30Sjl  * - CE-*            CE-(CE/OK). Scrub to determine if CE is permanent.
151225cf1a30Sjl  *                   If CE is permanent, inform SCF.  Once for each
151325cf1a30Sjl  *		     Dimm.  If CE becomes UE or CMPE, go back to above.
151425cf1a30Sjl  *
151525cf1a30Sjl  *
151625cf1a30Sjl  * MI (The error addresses for each bank are the same or different.)
151725cf1a30Sjl  * - Compair  error  If addresses are the same.  Just CMPE.
151825cf1a30Sjl  *		     If addresses are different (this could happen
151925cf1a30Sjl  *		     as a result of scrubbing.  Report each seperately.
152025cf1a30Sjl  *		     Only report error info on each side.
152125cf1a30Sjl  *
152225cf1a30Sjl  * - UE-UE           Addresses are the same.  Report MUE.
152325cf1a30Sjl  *		     Addresses are different.  Report SUE on each bank.
152425cf1a30Sjl  *		     Rewrite to clear UE.
152525cf1a30Sjl  *
152625cf1a30Sjl  * - UE-*	     UE-(CE/OK)
152725cf1a30Sjl  *		     Rewrite to clear UE.  Report SUE for the bank.
152825cf1a30Sjl  *
152925cf1a30Sjl  * - CE-*            CE-(CE/OK).  Scrub to determine if CE is permanent.
153025cf1a30Sjl  *                   If CE becomes UE or CMPE, go back to above.
153125cf1a30Sjl  *
153225cf1a30Sjl  */
153325cf1a30Sjl 
153425cf1a30Sjl static int
153525cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat)
153625cf1a30Sjl {
153725cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
153825cf1a30Sjl 	int i;
153925cf1a30Sjl 	int rv = 0;
154025cf1a30Sjl 
154125cf1a30Sjl 	MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n",
154225cf1a30Sjl 		flt_stat[0].mf_cntl, flt_stat[1].mf_cntl);
154325cf1a30Sjl 
154425cf1a30Sjl 	if (ptrl_error) {
154525cf1a30Sjl 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl)
154625cf1a30Sjl 			& MAC_CNTL_PTRL_ERRS) == 0)
154725cf1a30Sjl 			return (0);
154825cf1a30Sjl 	} else {
154925cf1a30Sjl 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl)
155025cf1a30Sjl 			& MAC_CNTL_MI_ERRS) == 0)
155125cf1a30Sjl 			return (0);
155225cf1a30Sjl 	}
155325cf1a30Sjl 
155425cf1a30Sjl 	/*
155525cf1a30Sjl 	 * First we take care of the case of CE
155625cf1a30Sjl 	 * because they can become UE or CMPE
155725cf1a30Sjl 	 */
155825cf1a30Sjl 	for (i = 0; i < 2; i++) {
155925cf1a30Sjl 		if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) {
156025cf1a30Sjl 			MC_LOG("CE detected on bank %d\n",
156125cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_bank);
156225cf1a30Sjl 			mc_scrub_ce(mcp, flt_stat[i].mf_flt_maddr.ma_bank,
156325cf1a30Sjl 				&flt_stat[i], ptrl_error);
156425cf1a30Sjl 			rv = 1;
156525cf1a30Sjl 		}
156625cf1a30Sjl 	}
156725cf1a30Sjl 
156825cf1a30Sjl 	/* The above scrubbing can turn CE into UE or CMPE */
156925cf1a30Sjl 
157025cf1a30Sjl 	/*
157125cf1a30Sjl 	 * Now we distinguish two cases: same address or not
157225cf1a30Sjl 	 * the same address.  It might seem more intuitive to
157325cf1a30Sjl 	 * distinguish PTRL v.s. MI error but it is more
157425cf1a30Sjl 	 * complicated that way.
157525cf1a30Sjl 	 */
157625cf1a30Sjl 
157725cf1a30Sjl 	if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) {
157825cf1a30Sjl 
157925cf1a30Sjl 		if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) ||
158025cf1a30Sjl 		    IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) {
158125cf1a30Sjl 			flt_stat[0].mf_type = FLT_TYPE_CMPE;
158225cf1a30Sjl 			flt_stat[1].mf_type = FLT_TYPE_CMPE;
158325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
158425cf1a30Sjl 			MC_LOG("cmpe error detected\n");
158525cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
158625cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
158725cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
158825cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
158925cf1a30Sjl 			mc_err_drain(mc_aflt);
159025cf1a30Sjl 			return (1);
159125cf1a30Sjl 		}
159225cf1a30Sjl 
159325cf1a30Sjl 		if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) &&
159425cf1a30Sjl 			IS_UE(flt_stat[1].mf_cntl, ptrl_error)) {
159525cf1a30Sjl 			/* Both side are UE's */
159625cf1a30Sjl 
159725cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[0]);
159825cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[1]);
159925cf1a30Sjl 			MC_LOG("MUE detected\n");
1600*0cc8ae86Sav 			flt_stat[0].mf_type = FLT_TYPE_MUE;
1601*0cc8ae86Sav 			flt_stat[1].mf_type = FLT_TYPE_MUE;
160225cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_MUE;
160325cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
160425cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
160525cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
160625cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
160725cf1a30Sjl 			mc_err_drain(mc_aflt);
160825cf1a30Sjl 			return (1);
160925cf1a30Sjl 		}
161025cf1a30Sjl 
161125cf1a30Sjl 		/* Now the only case is UE/CE, UE/OK, or don't care */
161225cf1a30Sjl 		for (i = 0; i < 2; i++) {
161325cf1a30Sjl 		    if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
1614*0cc8ae86Sav 
1615*0cc8ae86Sav 			/* rewrite can clear the one side UE error */
1616*0cc8ae86Sav 
161725cf1a30Sjl 			if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) {
161825cf1a30Sjl 				(void) do_rewrite(mcp,
161925cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_bank,
162025cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr);
162125cf1a30Sjl 			}
162225cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
162325cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
162425cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
162525cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
162625cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
162725cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
162825cf1a30Sjl 			mc_err_drain(mc_aflt);
162925cf1a30Sjl 			/* Once we hit a UE/CE or UE/OK case, done */
163025cf1a30Sjl 			return (1);
163125cf1a30Sjl 		    }
163225cf1a30Sjl 		}
163325cf1a30Sjl 
163425cf1a30Sjl 	} else {
163525cf1a30Sjl 		/*
163625cf1a30Sjl 		 * addresses are different. That means errors
163725cf1a30Sjl 		 * on the 2 banks are not related at all.
163825cf1a30Sjl 		 */
163925cf1a30Sjl 		for (i = 0; i < 2; i++) {
164025cf1a30Sjl 		    if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) {
164125cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_CMPE;
164225cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
164325cf1a30Sjl 			MC_LOG("cmpe error detected\n");
164425cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
164525cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
164625cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
164725cf1a30Sjl 			mc_err_drain(mc_aflt);
164825cf1a30Sjl 			/* no more report on this bank */
164925cf1a30Sjl 			flt_stat[i].mf_cntl = 0;
165025cf1a30Sjl 			rv = 1;
165125cf1a30Sjl 		    }
165225cf1a30Sjl 		}
165325cf1a30Sjl 
1654*0cc8ae86Sav 		/* rewrite can clear the one side UE error */
1655*0cc8ae86Sav 
165625cf1a30Sjl 		for (i = 0; i < 2; i++) {
165725cf1a30Sjl 		    if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
165825cf1a30Sjl 			(void) do_rewrite(mcp,
165925cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_bank,
166025cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_dimm_addr);
166125cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
166225cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
166325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
166425cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
166525cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
166625cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
166725cf1a30Sjl 			mc_err_drain(mc_aflt);
166825cf1a30Sjl 			rv = 1;
166925cf1a30Sjl 		    }
167025cf1a30Sjl 		}
167125cf1a30Sjl 	}
167225cf1a30Sjl 	return (rv);
167325cf1a30Sjl }
167425cf1a30Sjl static void
167525cf1a30Sjl mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr)
167625cf1a30Sjl {
167725cf1a30Sjl 	mc_aflt_t mc_aflt;
167825cf1a30Sjl 	mc_flt_stat_t flt_stat[2], mi_flt_stat[2];
1679*0cc8ae86Sav 	int i;
1680*0cc8ae86Sav 	int mi_valid;
168125cf1a30Sjl 
168225cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
168325cf1a30Sjl 	bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t));
168425cf1a30Sjl 	bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t));
168525cf1a30Sjl 
168625cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
168725cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
168825cf1a30Sjl 
168925cf1a30Sjl 	/* Now read all the registers into flt_stat */
169025cf1a30Sjl 
1691*0cc8ae86Sav 	for (i = 0; i < 2; i++) {
1692*0cc8ae86Sav 		MC_LOG("Reading registers of bank %d\n", bank);
1693*0cc8ae86Sav 		/* patrol registers */
1694*0cc8ae86Sav 		mc_read_ptrl_reg(mcp, bank, &flt_stat[i]);
169525cf1a30Sjl 
1696*0cc8ae86Sav 		ASSERT(maddr);
1697*0cc8ae86Sav 		maddr->mi_maddr = flt_stat[i].mf_flt_maddr;
169825cf1a30Sjl 
1699*0cc8ae86Sav 		MC_LOG("ptrl registers cntl %x add %x log %x\n",
1700*0cc8ae86Sav 			flt_stat[i].mf_cntl,
1701*0cc8ae86Sav 			flt_stat[i].mf_err_add,
1702*0cc8ae86Sav 			flt_stat[i].mf_err_log);
170325cf1a30Sjl 
1704*0cc8ae86Sav 		/* MI registers */
1705*0cc8ae86Sav 		mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]);
170625cf1a30Sjl 
1707*0cc8ae86Sav 		MC_LOG("MI registers cntl %x add %x log %x\n",
1708*0cc8ae86Sav 			mi_flt_stat[i].mf_cntl,
1709*0cc8ae86Sav 			mi_flt_stat[i].mf_err_add,
1710*0cc8ae86Sav 			mi_flt_stat[i].mf_err_log);
171125cf1a30Sjl 
1712*0cc8ae86Sav 		bank = bank^1;
1713*0cc8ae86Sav 	}
171425cf1a30Sjl 
171525cf1a30Sjl 	/* clear errors once we read all the registers */
1716*0cc8ae86Sav 	MAC_CLEAR_ERRS(mcp, bank,
171725cf1a30Sjl 		(MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
171825cf1a30Sjl 
1719*0cc8ae86Sav 	MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
1720*0cc8ae86Sav 
1721*0cc8ae86Sav 	/* Process MI errors first */
172225cf1a30Sjl 
1723*0cc8ae86Sav 	/* if not error mode, cntl1 is 0 */
1724*0cc8ae86Sav 	if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
1725*0cc8ae86Sav 		(mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
1726*0cc8ae86Sav 		mi_flt_stat[0].mf_cntl = 0;
1727*0cc8ae86Sav 
1728*0cc8ae86Sav 	if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
1729*0cc8ae86Sav 		(mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
1730*0cc8ae86Sav 		mi_flt_stat[1].mf_cntl = 0;
173125cf1a30Sjl 
1732*0cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
1733*0cc8ae86Sav 	mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]);
1734*0cc8ae86Sav 
1735*0cc8ae86Sav 	if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1736*0cc8ae86Sav 		MAC_CNTL_PTRL_ERR_SHIFT) ==
1737*0cc8ae86Sav 		((mi_flt_stat[0].mf_cntl & MAC_CNTL_MI_ERRS) >>
1738*0cc8ae86Sav 		MAC_CNTL_MI_ERR_SHIFT)) &&
1739*0cc8ae86Sav 		(flt_stat[0].mf_err_add == mi_flt_stat[0].mf_err_add) &&
1740*0cc8ae86Sav 		(((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1741*0cc8ae86Sav 		MAC_CNTL_PTRL_ERR_SHIFT) ==
1742*0cc8ae86Sav 		((mi_flt_stat[1].mf_cntl & MAC_CNTL_MI_ERRS) >>
1743*0cc8ae86Sav 		MAC_CNTL_MI_ERR_SHIFT)) &&
1744*0cc8ae86Sav 		(flt_stat[1].mf_err_add == mi_flt_stat[1].mf_err_add)) {
1745*0cc8ae86Sav #ifdef DEBUG
1746*0cc8ae86Sav 		MC_LOG("discarding PTRL error because "
1747*0cc8ae86Sav 		    "it is the same as MI\n");
1748*0cc8ae86Sav #endif
1749*0cc8ae86Sav 		maddr->mi_valid = mi_valid;
1750*0cc8ae86Sav 		return;
1751*0cc8ae86Sav 	}
175225cf1a30Sjl 	/* if not error mode, cntl1 is 0 */
175325cf1a30Sjl 	if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
175425cf1a30Sjl 		(flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
175525cf1a30Sjl 		flt_stat[0].mf_cntl = 0;
175625cf1a30Sjl 
175725cf1a30Sjl 	if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
175825cf1a30Sjl 		(flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
175925cf1a30Sjl 		flt_stat[1].mf_cntl = 0;
176025cf1a30Sjl 
176125cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
176225cf1a30Sjl 	maddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]);
176325cf1a30Sjl }
176425cf1a30Sjl static int
176525cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt,
176625cf1a30Sjl 	mc_flt_stat_t *flt_stat)
176725cf1a30Sjl {
176825cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
176925cf1a30Sjl 	int rv = 0;
177025cf1a30Sjl 
177125cf1a30Sjl 	mc_aflt->mflt_erpt_class = NULL;
177225cf1a30Sjl 	if (IS_UE(flt_stat->mf_cntl, ptrl_error)) {
177325cf1a30Sjl 		MC_LOG("UE deteceted\n");
177425cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_UE;
177525cf1a30Sjl 		mc_aflt->mflt_erpt_class = MC_OPL_UE;
177625cf1a30Sjl 		mc_aflt->mflt_pr = PR_UE;
177725cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
177825cf1a30Sjl 		rv = 1;
177925cf1a30Sjl 	} else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) {
178025cf1a30Sjl 		MC_LOG("CE deteceted\n");
178125cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
178225cf1a30Sjl 
178325cf1a30Sjl 		/* Error type can change after scrubing */
178425cf1a30Sjl 		mc_scrub_ce(mcp, bank, flt_stat, ptrl_error);
178525cf1a30Sjl 
178625cf1a30Sjl 		if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
178725cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CE;
178825cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
178925cf1a30Sjl 		} else if (flt_stat->mf_type == FLT_TYPE_UE) {
179025cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_UE;
179125cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
179225cf1a30Sjl 		}
179325cf1a30Sjl 		rv = 1;
179425cf1a30Sjl 	}
179525cf1a30Sjl 	MC_LOG("mc_process_error: fault type %x erpt %s\n",
179625cf1a30Sjl 		flt_stat->mf_type,
179725cf1a30Sjl 		mc_aflt->mflt_erpt_class);
179825cf1a30Sjl 	if (mc_aflt->mflt_erpt_class) {
179925cf1a30Sjl 		mc_aflt->mflt_stat[0] = flt_stat;
180025cf1a30Sjl 		mc_aflt->mflt_nflts = 1;
180125cf1a30Sjl 		mc_err_drain(mc_aflt);
180225cf1a30Sjl 	}
180325cf1a30Sjl 	return (rv);
180425cf1a30Sjl }
180525cf1a30Sjl 
180625cf1a30Sjl static void
180725cf1a30Sjl mc_error_handler(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr)
180825cf1a30Sjl {
180925cf1a30Sjl 	mc_aflt_t mc_aflt;
181025cf1a30Sjl 	mc_flt_stat_t flt_stat, mi_flt_stat;
1811*0cc8ae86Sav 	int mi_valid;
181225cf1a30Sjl 
181325cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
181425cf1a30Sjl 	bzero(&flt_stat, sizeof (mc_flt_stat_t));
181525cf1a30Sjl 	bzero(&mi_flt_stat, sizeof (mc_flt_stat_t));
181625cf1a30Sjl 
181725cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
181825cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
181925cf1a30Sjl 
182025cf1a30Sjl 	/* patrol registers */
182125cf1a30Sjl 	mc_read_ptrl_reg(mcp, bank, &flt_stat);
182225cf1a30Sjl 
182325cf1a30Sjl 	ASSERT(maddr);
182425cf1a30Sjl 	maddr->mi_maddr = flt_stat.mf_flt_maddr;
182525cf1a30Sjl 
182625cf1a30Sjl 	MC_LOG("ptrl registers cntl %x add %x log %x\n",
182725cf1a30Sjl 		flt_stat.mf_cntl,
182825cf1a30Sjl 		flt_stat.mf_err_add,
182925cf1a30Sjl 		flt_stat.mf_err_log);
183025cf1a30Sjl 
183125cf1a30Sjl 	/* MI registers */
183225cf1a30Sjl 	mc_read_mi_reg(mcp, bank, &mi_flt_stat);
183325cf1a30Sjl 
1834*0cc8ae86Sav 
183525cf1a30Sjl 	MC_LOG("MI registers cntl %x add %x log %x\n",
183625cf1a30Sjl 		mi_flt_stat.mf_cntl,
183725cf1a30Sjl 		mi_flt_stat.mf_err_add,
183825cf1a30Sjl 		mi_flt_stat.mf_err_log);
183925cf1a30Sjl 
184025cf1a30Sjl 	/* clear errors once we read all the registers */
184125cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
184225cf1a30Sjl 
1843*0cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
1844*0cc8ae86Sav 	if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) &&
1845*0cc8ae86Sav 		((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
1846*0cc8ae86Sav 		((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
1847*0cc8ae86Sav 		mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat);
1848*0cc8ae86Sav 	}
1849*0cc8ae86Sav 
1850*0cc8ae86Sav 	if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1851*0cc8ae86Sav 		MAC_CNTL_PTRL_ERR_SHIFT) ==
1852*0cc8ae86Sav 		((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) >>
1853*0cc8ae86Sav 		MAC_CNTL_MI_ERR_SHIFT)) &&
1854*0cc8ae86Sav 		(flt_stat.mf_err_add == mi_flt_stat.mf_err_add)) {
1855*0cc8ae86Sav #ifdef DEBUG
1856*0cc8ae86Sav 		MC_LOG("discarding PTRL error because "
1857*0cc8ae86Sav 		    "it is the same as MI\n");
1858*0cc8ae86Sav #endif
1859*0cc8ae86Sav 		maddr->mi_valid = mi_valid;
1860*0cc8ae86Sav 		return;
1861*0cc8ae86Sav 	}
1862*0cc8ae86Sav 
186325cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
186425cf1a30Sjl 	if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) &&
186525cf1a30Sjl 		((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
186625cf1a30Sjl 		((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
186725cf1a30Sjl 		maddr->mi_valid = mc_process_error(mcp, bank,
186825cf1a30Sjl 			&mc_aflt, &flt_stat);
186925cf1a30Sjl 	}
187025cf1a30Sjl }
187125cf1a30Sjl /*
187225cf1a30Sjl  *	memory patrol error handling algorithm:
187325cf1a30Sjl  *	timeout() is used to do periodic polling
187425cf1a30Sjl  *	This is the flow chart.
187525cf1a30Sjl  *	timeout ->
187625cf1a30Sjl  *	mc_check_errors()
187725cf1a30Sjl  *	    if memory bank is installed, read the status register
187825cf1a30Sjl  *	    if any error bit is set,
187925cf1a30Sjl  *	    -> mc_error_handler()
188025cf1a30Sjl  *		-> read all error regsiters
188125cf1a30Sjl  *	        -> mc_process_error()
188225cf1a30Sjl  *	            determine error type
188325cf1a30Sjl  *	            rewrite to clear error or scrub to determine CE type
188425cf1a30Sjl  *	            inform SCF on permanent CE
188525cf1a30Sjl  *	        -> mc_err_drain
188625cf1a30Sjl  *	            page offline processing
188725cf1a30Sjl  *	            -> mc_ereport_post()
188825cf1a30Sjl  */
188925cf1a30Sjl 
189025cf1a30Sjl static void
189125cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp)
189225cf1a30Sjl {
189325cf1a30Sjl 	mc_addr_info_t maddr_info;
189425cf1a30Sjl 	int i, error_count = 0;
189525cf1a30Sjl 	uint32_t stat, cntl;
1896*0cc8ae86Sav 	int running;
189725cf1a30Sjl 
189825cf1a30Sjl 	/*
189925cf1a30Sjl 	 * scan errors.
190025cf1a30Sjl 	 */
1901*0cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS)
1902*0cc8ae86Sav 		return;
1903*0cc8ae86Sav 
190425cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
190525cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
190625cf1a30Sjl 			stat = ldphysio(MAC_PTRL_STAT(mcp, i));
190725cf1a30Sjl 			cntl = ldphysio(MAC_PTRL_CNTL(mcp, i));
1908*0cc8ae86Sav 			running = cntl & MAC_CNTL_PTRL_START;
1909*0cc8ae86Sav 
191025cf1a30Sjl 			if (cntl & MAC_CNTL_PTRL_ADD_MAX) {
1911*0cc8ae86Sav 				mcp->mc_period[i]++;
191225cf1a30Sjl 				MC_LOG("mc period %ld on "
1913*0cc8ae86Sav 				    "/LSB%d/B%d\n", mcp->mc_period[i],
191425cf1a30Sjl 				    mcp->mc_board_num, i);
191525cf1a30Sjl 				MAC_CLEAR_MAX(mcp, i);
1916*0cc8ae86Sav 				if (mcp->mc_speedup_period[i] > 0) {
1917*0cc8ae86Sav 				/* If patrol is stoppped, we fall through */
1918*0cc8ae86Sav 					if (--mcp->mc_speedup_period[i] == 0 &&
1919*0cc8ae86Sav 						running) {
1920*0cc8ae86Sav 					    MAC_CMD(mcp, i, 0);
1921*0cc8ae86Sav 					}
1922*0cc8ae86Sav 				}
192325cf1a30Sjl 			}
192425cf1a30Sjl 			if (mc_debug_show_all) {
192525cf1a30Sjl 				MC_LOG("/LSB%d/B%d stat %x cntl %x\n",
192625cf1a30Sjl 					mcp->mc_board_num, i,
192725cf1a30Sjl 					stat, cntl);
192825cf1a30Sjl 			}
192925cf1a30Sjl 			if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) {
1930*0cc8ae86Sav 				if (running) {
1931*0cc8ae86Sav 				    MC_LOG("patrol running /LSB%d/B%d\n",
1932*0cc8ae86Sav 						mcp->mc_board_num, i);
1933*0cc8ae86Sav 				}
1934*0cc8ae86Sav 				if (running) {
1935*0cc8ae86Sav 					/* speed up the scanning */
1936*0cc8ae86Sav 					mcp->mc_speedup_period[i] = 2;
1937*0cc8ae86Sav 					MAC_CMD(mcp, i, 0);
1938*0cc8ae86Sav 				} else {
1939*0cc8ae86Sav 				    mcp->mc_speedup_period[i] = 0;
1940*0cc8ae86Sav 				    maddr_info.mi_valid = 0;
1941*0cc8ae86Sav 				    maddr_info.mi_advance = 1;
1942*0cc8ae86Sav 				    if (IS_MIRROR(mcp, i))
194325cf1a30Sjl 					mc_error_handler_mir(mcp, i,
194425cf1a30Sjl 						&maddr_info);
1945*0cc8ae86Sav 				    else
194625cf1a30Sjl 					mc_error_handler(mcp, i, &maddr_info);
194725cf1a30Sjl 
1948*0cc8ae86Sav 				    error_count++;
1949*0cc8ae86Sav 				    restart_patrol(mcp, i, &maddr_info);
1950*0cc8ae86Sav 				}
195125cf1a30Sjl 			} else {
195225cf1a30Sjl 				restart_patrol(mcp, i, NULL);
195325cf1a30Sjl 			}
195425cf1a30Sjl 		}
195525cf1a30Sjl 	}
195625cf1a30Sjl 	if (error_count > 0)
195725cf1a30Sjl 		mcp->mc_last_error += error_count;
195825cf1a30Sjl 	else
195925cf1a30Sjl 		mcp->mc_last_error = 0;
196025cf1a30Sjl }
196125cf1a30Sjl 
1962*0cc8ae86Sav /*
1963*0cc8ae86Sav  * mc_polling -- Check errors for only one instance,
1964*0cc8ae86Sav  * but process errors for all instances to make sure we drain the errors
1965*0cc8ae86Sav  * faster than they can be accumulated.
1966*0cc8ae86Sav  *
1967*0cc8ae86Sav  * Polling on each board should be done only once per each
1968*0cc8ae86Sav  * mc_patrol_interval_sec.  This is equivalent to setting mc_tick_left
1969*0cc8ae86Sav  * to OPL_MAX_BOARDS and decrement by 1 on each timeout.
1970*0cc8ae86Sav  * Once mc_tick_left becomes negative, the board becomes a candidate
1971*0cc8ae86Sav  * for polling because it has waited for at least
1972*0cc8ae86Sav  * mc_patrol_interval_sec's long.    If mc_timeout_period is calculated
1973*0cc8ae86Sav  * differently, this has to beupdated accordingly.
1974*0cc8ae86Sav  */
197525cf1a30Sjl 
197625cf1a30Sjl static void
1977*0cc8ae86Sav mc_polling(void)
197825cf1a30Sjl {
1979*0cc8ae86Sav 	int i, scan_error;
1980*0cc8ae86Sav 	mc_opl_t *mcp;
198125cf1a30Sjl 
198225cf1a30Sjl 
1983*0cc8ae86Sav 	scan_error = 1;
1984*0cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
1985*0cc8ae86Sav 		mutex_enter(&mcmutex);
1986*0cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL) {
1987*0cc8ae86Sav 			mutex_exit(&mcmutex);
1988*0cc8ae86Sav 			continue;
1989*0cc8ae86Sav 		}
1990*0cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
1991*0cc8ae86Sav 		mutex_exit(&mcmutex);
1992*0cc8ae86Sav 		if (scan_error && mcp->mc_tick_left <= 0) {
1993*0cc8ae86Sav 			mc_check_errors_func((void *)mcp);
1994*0cc8ae86Sav 			mcp->mc_tick_left = OPL_MAX_BOARDS;
1995*0cc8ae86Sav 			scan_error = 0;
1996*0cc8ae86Sav 		} else {
1997*0cc8ae86Sav 			mcp->mc_tick_left--;
1998*0cc8ae86Sav 		}
1999*0cc8ae86Sav 		mc_process_scf_log(mcp);
2000*0cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
200125cf1a30Sjl 	}
200225cf1a30Sjl }
200325cf1a30Sjl 
200425cf1a30Sjl static void
200525cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr)
200625cf1a30Sjl {
200725cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
200825cf1a30Sjl 	maddr->ma_bank = bank;
200925cf1a30Sjl 	maddr->ma_dimm_addr = 0;
201025cf1a30Sjl }
201125cf1a30Sjl 
201225cf1a30Sjl typedef struct mc_mem_range {
201325cf1a30Sjl 	uint64_t	addr;
201425cf1a30Sjl 	uint64_t	size;
201525cf1a30Sjl } mc_mem_range_t;
201625cf1a30Sjl 
201725cf1a30Sjl static int
201825cf1a30Sjl get_base_address(mc_opl_t *mcp)
201925cf1a30Sjl {
202025cf1a30Sjl 	mc_mem_range_t *mem_range;
202125cf1a30Sjl 	int len;
202225cf1a30Sjl 
202325cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
202425cf1a30Sjl 		"sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) {
202525cf1a30Sjl 		return (DDI_FAILURE);
202625cf1a30Sjl 	}
202725cf1a30Sjl 
202825cf1a30Sjl 	mcp->mc_start_address = mem_range->addr;
202925cf1a30Sjl 	mcp->mc_size = mem_range->size;
203025cf1a30Sjl 
203125cf1a30Sjl 	kmem_free(mem_range, len);
203225cf1a30Sjl 	return (DDI_SUCCESS);
203325cf1a30Sjl }
203425cf1a30Sjl 
203525cf1a30Sjl struct mc_addr_spec {
203625cf1a30Sjl 	uint32_t bank;
203725cf1a30Sjl 	uint32_t phys_hi;
203825cf1a30Sjl 	uint32_t phys_lo;
203925cf1a30Sjl };
204025cf1a30Sjl 
204125cf1a30Sjl #define	REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo)
204225cf1a30Sjl 
204325cf1a30Sjl static char *mc_tbl_name[] = {
204425cf1a30Sjl 	"cs0-mc-pa-trans-table",
204525cf1a30Sjl 	"cs1-mc-pa-trans-table"
204625cf1a30Sjl };
204725cf1a30Sjl 
204825cf1a30Sjl static int
204925cf1a30Sjl mc_valid_pa(mc_opl_t *mcp, uint64_t pa)
205025cf1a30Sjl {
205125cf1a30Sjl 	struct memlist *ml;
205225cf1a30Sjl 
205325cf1a30Sjl 	if (mcp->mlist == NULL)
205425cf1a30Sjl 		mc_get_mlist(mcp);
205525cf1a30Sjl 
205625cf1a30Sjl 	for (ml = mcp->mlist; ml; ml = ml->next) {
205725cf1a30Sjl 		if (ml->address <= pa && pa < (ml->address + ml->size))
205825cf1a30Sjl 			return (1);
205925cf1a30Sjl 	}
206025cf1a30Sjl 	return (0);
206125cf1a30Sjl }
206225cf1a30Sjl 
206325cf1a30Sjl static void
206425cf1a30Sjl mc_memlist_delete(struct memlist *mlist)
206525cf1a30Sjl {
206625cf1a30Sjl 	struct memlist *ml;
206725cf1a30Sjl 
206825cf1a30Sjl 	for (ml = mlist; ml; ml = mlist) {
206925cf1a30Sjl 		mlist = ml->next;
207025cf1a30Sjl 		kmem_free(ml, sizeof (struct memlist));
207125cf1a30Sjl 	}
207225cf1a30Sjl }
207325cf1a30Sjl 
207425cf1a30Sjl static struct memlist *
207525cf1a30Sjl mc_memlist_dup(struct memlist *mlist)
207625cf1a30Sjl {
207725cf1a30Sjl 	struct memlist *hl = NULL, *tl, **mlp;
207825cf1a30Sjl 
207925cf1a30Sjl 	if (mlist == NULL)
208025cf1a30Sjl 		return (NULL);
208125cf1a30Sjl 
208225cf1a30Sjl 	mlp = &hl;
208325cf1a30Sjl 	tl = *mlp;
208425cf1a30Sjl 	for (; mlist; mlist = mlist->next) {
208525cf1a30Sjl 		*mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP);
208625cf1a30Sjl 		(*mlp)->address = mlist->address;
208725cf1a30Sjl 		(*mlp)->size = mlist->size;
208825cf1a30Sjl 		(*mlp)->prev = tl;
208925cf1a30Sjl 		tl = *mlp;
209025cf1a30Sjl 		mlp = &((*mlp)->next);
209125cf1a30Sjl 	}
209225cf1a30Sjl 	*mlp = NULL;
209325cf1a30Sjl 
209425cf1a30Sjl 	return (hl);
209525cf1a30Sjl }
209625cf1a30Sjl 
209725cf1a30Sjl 
209825cf1a30Sjl static struct memlist *
209925cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len)
210025cf1a30Sjl {
210125cf1a30Sjl 	uint64_t	end;
210225cf1a30Sjl 	struct memlist	*ml, *tl, *nlp;
210325cf1a30Sjl 
210425cf1a30Sjl 	if (mlist == NULL)
210525cf1a30Sjl 		return (NULL);
210625cf1a30Sjl 
210725cf1a30Sjl 	end = base + len;
210825cf1a30Sjl 	if ((end <= mlist->address) || (base == end))
210925cf1a30Sjl 		return (mlist);
211025cf1a30Sjl 
211125cf1a30Sjl 	for (tl = ml = mlist; ml; tl = ml, ml = nlp) {
211225cf1a30Sjl 		uint64_t	mend;
211325cf1a30Sjl 
211425cf1a30Sjl 		nlp = ml->next;
211525cf1a30Sjl 
211625cf1a30Sjl 		if (end <= ml->address)
211725cf1a30Sjl 			break;
211825cf1a30Sjl 
211925cf1a30Sjl 		mend = ml->address + ml->size;
212025cf1a30Sjl 		if (base < mend) {
212125cf1a30Sjl 			if (base <= ml->address) {
212225cf1a30Sjl 				ml->address = end;
212325cf1a30Sjl 				if (end >= mend)
212425cf1a30Sjl 					ml->size = 0ull;
212525cf1a30Sjl 				else
212625cf1a30Sjl 					ml->size = mend - ml->address;
212725cf1a30Sjl 			} else {
212825cf1a30Sjl 				ml->size = base - ml->address;
212925cf1a30Sjl 				if (end < mend) {
213025cf1a30Sjl 					struct memlist	*nl;
213125cf1a30Sjl 					/*
213225cf1a30Sjl 					 * splitting an memlist entry.
213325cf1a30Sjl 					 */
213425cf1a30Sjl 					nl = kmem_alloc(sizeof (struct memlist),
213525cf1a30Sjl 						KM_SLEEP);
213625cf1a30Sjl 					nl->address = end;
213725cf1a30Sjl 					nl->size = mend - nl->address;
213825cf1a30Sjl 					if ((nl->next = nlp) != NULL)
213925cf1a30Sjl 						nlp->prev = nl;
214025cf1a30Sjl 					nl->prev = ml;
214125cf1a30Sjl 					ml->next = nl;
214225cf1a30Sjl 					nlp = nl;
214325cf1a30Sjl 				}
214425cf1a30Sjl 			}
214525cf1a30Sjl 			if (ml->size == 0ull) {
214625cf1a30Sjl 				if (ml == mlist) {
214725cf1a30Sjl 					if ((mlist = nlp) != NULL)
214825cf1a30Sjl 						nlp->prev = NULL;
214925cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
215025cf1a30Sjl 					if (mlist == NULL)
215125cf1a30Sjl 						break;
215225cf1a30Sjl 					ml = nlp;
215325cf1a30Sjl 				} else {
215425cf1a30Sjl 					if ((tl->next = nlp) != NULL)
215525cf1a30Sjl 						nlp->prev = tl;
215625cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
215725cf1a30Sjl 					ml = tl;
215825cf1a30Sjl 				}
215925cf1a30Sjl 			}
216025cf1a30Sjl 		}
216125cf1a30Sjl 	}
216225cf1a30Sjl 
216325cf1a30Sjl 	return (mlist);
216425cf1a30Sjl }
216525cf1a30Sjl 
216625cf1a30Sjl static void
216725cf1a30Sjl mc_get_mlist(mc_opl_t *mcp)
216825cf1a30Sjl {
216925cf1a30Sjl 	struct memlist *mlist;
217025cf1a30Sjl 
217125cf1a30Sjl 	memlist_read_lock();
217225cf1a30Sjl 	mlist = mc_memlist_dup(phys_install);
217325cf1a30Sjl 	memlist_read_unlock();
217425cf1a30Sjl 
217525cf1a30Sjl 	if (mlist) {
217625cf1a30Sjl 		mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address);
217725cf1a30Sjl 	}
217825cf1a30Sjl 
217925cf1a30Sjl 	if (mlist) {
218025cf1a30Sjl 		uint64_t startpa, endpa;
218125cf1a30Sjl 
218225cf1a30Sjl 		startpa = mcp->mc_start_address + mcp->mc_size;
218325cf1a30Sjl 		endpa = ptob(physmax + 1);
218425cf1a30Sjl 		if (endpa > startpa) {
218525cf1a30Sjl 			mlist = mc_memlist_del_span(mlist,
218625cf1a30Sjl 				startpa, endpa - startpa);
218725cf1a30Sjl 		}
218825cf1a30Sjl 	}
218925cf1a30Sjl 
219025cf1a30Sjl 	if (mlist) {
219125cf1a30Sjl 		mcp->mlist = mlist;
219225cf1a30Sjl 	}
219325cf1a30Sjl }
219425cf1a30Sjl 
219525cf1a30Sjl int
219625cf1a30Sjl mc_board_add(mc_opl_t *mcp)
219725cf1a30Sjl {
219825cf1a30Sjl 	struct mc_addr_spec *macaddr;
2199*0cc8ae86Sav 	cs_status_t *cs_status;
2200*0cc8ae86Sav 	int len, len1, i, bk, cc;
220125cf1a30Sjl 	mc_addr_info_t maddr;
220225cf1a30Sjl 	uint32_t mirr;
2203*0cc8ae86Sav 	int nbanks = 0;
2204*0cc8ae86Sav 	uint64_t nbytes = 0;
220525cf1a30Sjl 
220625cf1a30Sjl 	/*
220725cf1a30Sjl 	 * Get configurations from "pseudo-mc" node which includes:
220825cf1a30Sjl 	 * board# : LSB number
220925cf1a30Sjl 	 * mac-addr : physical base address of MAC registers
221025cf1a30Sjl 	 * csX-mac-pa-trans-table: translation table from DIMM address
221125cf1a30Sjl 	 *			to physical address or vice versa.
221225cf1a30Sjl 	 */
221325cf1a30Sjl 	mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip,
221425cf1a30Sjl 		DDI_PROP_DONTPASS, "board#", -1);
221525cf1a30Sjl 
2216*0cc8ae86Sav 	if (mcp->mc_board_num == -1) {
2217*0cc8ae86Sav 		return (DDI_FAILURE);
2218*0cc8ae86Sav 	}
2219*0cc8ae86Sav 
222025cf1a30Sjl 	/*
222125cf1a30Sjl 	 * Get start address in this CAB. It can be gotten from
222225cf1a30Sjl 	 * "sb-mem-ranges" property.
222325cf1a30Sjl 	 */
222425cf1a30Sjl 
222525cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
222625cf1a30Sjl 		return (DDI_FAILURE);
222725cf1a30Sjl 	}
222825cf1a30Sjl 	/* get mac-pa trans tables */
222925cf1a30Sjl 	for (i = 0; i < MC_TT_CS; i++) {
223025cf1a30Sjl 		len = MC_TT_ENTRIES;
223125cf1a30Sjl 		cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip,
223225cf1a30Sjl 			DDI_PROP_DONTPASS, mc_tbl_name[i],
223325cf1a30Sjl 			(caddr_t)mcp->mc_trans_table[i], &len);
223425cf1a30Sjl 
223525cf1a30Sjl 		if (cc != DDI_SUCCESS) {
223625cf1a30Sjl 			bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES);
223725cf1a30Sjl 		}
223825cf1a30Sjl 	}
223925cf1a30Sjl 	mcp->mlist = NULL;
224025cf1a30Sjl 
224125cf1a30Sjl 	mc_get_mlist(mcp);
224225cf1a30Sjl 
224325cf1a30Sjl 	/* initialize bank informations */
224425cf1a30Sjl 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
224525cf1a30Sjl 		"mc-addr", (caddr_t)&macaddr, &len);
224625cf1a30Sjl 	if (cc != DDI_SUCCESS) {
224725cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc);
224825cf1a30Sjl 		return (DDI_FAILURE);
224925cf1a30Sjl 	}
225025cf1a30Sjl 
2251*0cc8ae86Sav 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
2252*0cc8ae86Sav 		"cs-status", (caddr_t)&cs_status, &len1);
225325cf1a30Sjl 
2254*0cc8ae86Sav 	if (cc != DDI_SUCCESS) {
2255*0cc8ae86Sav 		if (len > 0)
2256*0cc8ae86Sav 			kmem_free(macaddr, len);
2257*0cc8ae86Sav 		cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc);
2258*0cc8ae86Sav 		return (DDI_FAILURE);
2259*0cc8ae86Sav 	}
226025cf1a30Sjl 
2261*0cc8ae86Sav 	mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL);
2262*0cc8ae86Sav 
2263*0cc8ae86Sav 	for (i = 0; i < len1 / sizeof (cs_status_t); i++) {
2264*0cc8ae86Sav 		nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) |
2265*0cc8ae86Sav 			((uint64_t)cs_status[i].cs_avail_low);
2266*0cc8ae86Sav 	}
2267*0cc8ae86Sav 	if (len1 > 0)
2268*0cc8ae86Sav 		kmem_free(cs_status, len1);
2269*0cc8ae86Sav 	nbanks = len / sizeof (struct mc_addr_spec);
2270*0cc8ae86Sav 
2271*0cc8ae86Sav 	if (nbanks > 0)
2272*0cc8ae86Sav 		nbytes /= nbanks;
2273*0cc8ae86Sav 	else {
2274*0cc8ae86Sav 		/* No need to free macaddr because len must be 0 */
2275*0cc8ae86Sav 		mcp->mc_status |= MC_MEMORYLESS;
2276*0cc8ae86Sav 		return (DDI_SUCCESS);
2277*0cc8ae86Sav 	}
2278*0cc8ae86Sav 
2279*0cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
2280*0cc8ae86Sav 		mcp->mc_scf_retry[i] = 0;
2281*0cc8ae86Sav 		mcp->mc_period[i] = 0;
2282*0cc8ae86Sav 		mcp->mc_speedup_period[i] = 0;
2283*0cc8ae86Sav 	}
2284*0cc8ae86Sav 
2285*0cc8ae86Sav 	/*
2286*0cc8ae86Sav 	 * Get the memory size here. Let it be B (bytes).
2287*0cc8ae86Sav 	 * Let T be the time in u.s. to scan 64 bytes.
2288*0cc8ae86Sav 	 * If we want to complete 1 round of scanning in P seconds.
2289*0cc8ae86Sav 	 *
2290*0cc8ae86Sav 	 *	B * T * 10^(-6)	= P
2291*0cc8ae86Sav 	 *	---------------
2292*0cc8ae86Sav 	 *		64
2293*0cc8ae86Sav 	 *
2294*0cc8ae86Sav 	 *	T = P * 64 * 10^6
2295*0cc8ae86Sav 	 *	    -------------
2296*0cc8ae86Sav 	 *		B
2297*0cc8ae86Sav 	 *
2298*0cc8ae86Sav 	 *	  = P * 64 * 10^6
2299*0cc8ae86Sav 	 *	    -------------
2300*0cc8ae86Sav 	 *		B
2301*0cc8ae86Sav 	 *
2302*0cc8ae86Sav 	 *	The timing bits are set in PTRL_CNTL[28:26] where
2303*0cc8ae86Sav 	 *
2304*0cc8ae86Sav 	 *	0	- 1 m.s
2305*0cc8ae86Sav 	 *	1	- 512 u.s.
2306*0cc8ae86Sav 	 *	10	- 256 u.s.
2307*0cc8ae86Sav 	 *	11	- 128 u.s.
2308*0cc8ae86Sav 	 *	100	- 64 u.s.
2309*0cc8ae86Sav 	 *	101	- 32 u.s.
2310*0cc8ae86Sav 	 *	110	- 0 u.s.
2311*0cc8ae86Sav 	 *	111	- reserved.
2312*0cc8ae86Sav 	 *
2313*0cc8ae86Sav 	 *
2314*0cc8ae86Sav 	 *	a[0] = 110, a[1] = 101, ... a[6] = 0
2315*0cc8ae86Sav 	 *
2316*0cc8ae86Sav 	 *	cs-status property is int x 7
2317*0cc8ae86Sav 	 *	0 - cs#
2318*0cc8ae86Sav 	 *	1 - cs-status
2319*0cc8ae86Sav 	 *	2 - cs-avail.hi
2320*0cc8ae86Sav 	 *	3 - cs-avail.lo
2321*0cc8ae86Sav 	 *	4 - dimm-capa.hi
2322*0cc8ae86Sav 	 *	5 - dimm-capa.lo
2323*0cc8ae86Sav 	 *	6 - #of dimms
2324*0cc8ae86Sav 	 */
2325*0cc8ae86Sav 
2326*0cc8ae86Sav 	if (nbytes > 0) {
2327*0cc8ae86Sav 		int i;
2328*0cc8ae86Sav 		uint64_t ms;
2329*0cc8ae86Sav 		ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes;
2330*0cc8ae86Sav 		mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds;
2331*0cc8ae86Sav 		for (i = 0; i < MC_MAX_SPEEDS - 1; i++) {
2332*0cc8ae86Sav 			if (ms < mc_scan_speeds[i + 1].mc_period) {
2333*0cc8ae86Sav 				mcp->mc_speed = mc_scan_speeds[i].mc_speeds;
2334*0cc8ae86Sav 				break;
2335*0cc8ae86Sav 			}
2336*0cc8ae86Sav 		}
2337*0cc8ae86Sav 	} else
2338*0cc8ae86Sav 		mcp->mc_speed = 0;
2339*0cc8ae86Sav 
2340*0cc8ae86Sav 
2341*0cc8ae86Sav 	for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) {
2342*0cc8ae86Sav 		struct mc_bank *bankp;
2343*0cc8ae86Sav 		uint32_t reg;
2344*0cc8ae86Sav 
2345*0cc8ae86Sav 		/*
2346*0cc8ae86Sav 		 * setup bank
2347*0cc8ae86Sav 		 */
2348*0cc8ae86Sav 		bk = macaddr[i].bank;
2349*0cc8ae86Sav 		bankp = &(mcp->mc_bank[bk]);
2350*0cc8ae86Sav 		bankp->mcb_status = BANK_INSTALLED;
2351*0cc8ae86Sav 		bankp->mcb_reg_base = REGS_PA(macaddr, i);
2352*0cc8ae86Sav 
2353*0cc8ae86Sav 		reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk));
2354*0cc8ae86Sav 		bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS);
235525cf1a30Sjl 
235625cf1a30Sjl 		/*
235725cf1a30Sjl 		 * check if mirror mode
235825cf1a30Sjl 		 */
235925cf1a30Sjl 		mirr = LD_MAC_REG(MAC_MIRR(mcp, bk));
236025cf1a30Sjl 
236125cf1a30Sjl 		if (mirr & MAC_MIRR_MIRROR_MODE) {
236225cf1a30Sjl 			MC_LOG("Mirror -> /LSB%d/B%d\n",
236325cf1a30Sjl 				mcp->mc_board_num, bk);
236425cf1a30Sjl 			bankp->mcb_status |= BANK_MIRROR_MODE;
236525cf1a30Sjl 			/*
236625cf1a30Sjl 			 * The following bit is only used for
236725cf1a30Sjl 			 * error injection.  We should clear it
236825cf1a30Sjl 			 */
236925cf1a30Sjl 			if (mirr & MAC_MIRR_BANK_EXCLUSIVE)
237025cf1a30Sjl 				ST_MAC_REG(MAC_MIRR(mcp, bk),
237125cf1a30Sjl 					0);
237225cf1a30Sjl 		}
237325cf1a30Sjl 
237425cf1a30Sjl 		/*
237525cf1a30Sjl 		 * restart if not mirror mode or the other bank
237625cf1a30Sjl 		 * of the mirror is not running
237725cf1a30Sjl 		 */
237825cf1a30Sjl 		if (!(mirr & MAC_MIRR_MIRROR_MODE) ||
237925cf1a30Sjl 			!(mcp->mc_bank[bk^1].mcb_status &
238025cf1a30Sjl 			BANK_PTRL_RUNNING)) {
238125cf1a30Sjl 			MC_LOG("Starting up /LSB%d/B%d\n",
238225cf1a30Sjl 				mcp->mc_board_num, bk);
238325cf1a30Sjl 			get_ptrl_start_address(mcp, bk, &maddr.mi_maddr);
2384*0cc8ae86Sav 			maddr.mi_maddr.ma_bd = mcp->mc_board_num;
2385*0cc8ae86Sav 			maddr.mi_maddr.ma_bank = bk;
2386*0cc8ae86Sav 			maddr.mi_maddr.ma_dimm_addr = 0;
2387*0cc8ae86Sav 			maddr.mi_valid = 0;
238825cf1a30Sjl 			maddr.mi_advance = 0;
238925cf1a30Sjl 			restart_patrol(mcp, bk, &maddr);
239025cf1a30Sjl 		} else {
239125cf1a30Sjl 			MC_LOG("Not starting up /LSB%d/B%d\n",
239225cf1a30Sjl 				mcp->mc_board_num, bk);
239325cf1a30Sjl 		}
239425cf1a30Sjl 		bankp->mcb_status |= BANK_PTRL_RUNNING;
239525cf1a30Sjl 	}
2396*0cc8ae86Sav 	if (len > 0)
2397*0cc8ae86Sav 		kmem_free(macaddr, len);
2398*0cc8ae86Sav 
2399*0cc8ae86Sav 	mcp->mc_dimm_list = mc_get_dimm_list(mcp);
240025cf1a30Sjl 
240125cf1a30Sjl 	/*
240225cf1a30Sjl 	 * set interval in HZ.
240325cf1a30Sjl 	 */
240425cf1a30Sjl 	mcp->mc_last_error = 0;
240525cf1a30Sjl 
240625cf1a30Sjl 	/* restart memory patrol checking */
240725cf1a30Sjl 	mcp->mc_status |= MC_POLL_RUNNING;
240825cf1a30Sjl 
240925cf1a30Sjl 	return (DDI_SUCCESS);
241025cf1a30Sjl }
241125cf1a30Sjl 
241225cf1a30Sjl int
241325cf1a30Sjl mc_board_del(mc_opl_t *mcp)
241425cf1a30Sjl {
241525cf1a30Sjl 	int i;
241625cf1a30Sjl 	scf_log_t *p;
241725cf1a30Sjl 
241825cf1a30Sjl 	/*
241925cf1a30Sjl 	 * cleanup mac state
242025cf1a30Sjl 	 */
242125cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
2422*0cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
2423*0cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
2424*0cc8ae86Sav 		mutex_destroy(&mcp->mc_lock);
2425*0cc8ae86Sav 		return (DDI_SUCCESS);
2426*0cc8ae86Sav 	}
242725cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
242825cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
242925cf1a30Sjl 			mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED;
243025cf1a30Sjl 		}
243125cf1a30Sjl 	}
243225cf1a30Sjl 
243325cf1a30Sjl 	/* stop memory patrol checking */
243425cf1a30Sjl 	if (mcp->mc_status & MC_POLL_RUNNING) {
243525cf1a30Sjl 		mcp->mc_status &= ~MC_POLL_RUNNING;
243625cf1a30Sjl 	}
243725cf1a30Sjl 
243825cf1a30Sjl 	/* just throw away all the scf logs */
2439*0cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
2440*0cc8ae86Sav 	    while ((p = mcp->mc_scf_log[i]) != NULL) {
2441*0cc8ae86Sav 		mcp->mc_scf_log[i] = p->sl_next;
2442*0cc8ae86Sav 		mcp->mc_scf_total[i]--;
244325cf1a30Sjl 		kmem_free(p, sizeof (scf_log_t));
2444*0cc8ae86Sav 	    }
244525cf1a30Sjl 	}
244625cf1a30Sjl 
244725cf1a30Sjl 	if (mcp->mlist)
244825cf1a30Sjl 		mc_memlist_delete(mcp->mlist);
244925cf1a30Sjl 
2450*0cc8ae86Sav 	if (mcp->mc_dimm_list)
2451*0cc8ae86Sav 		mc_free_dimm_list(mcp->mc_dimm_list);
2452*0cc8ae86Sav 
245325cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
245425cf1a30Sjl 
245525cf1a30Sjl 	mutex_destroy(&mcp->mc_lock);
245625cf1a30Sjl 	return (DDI_SUCCESS);
245725cf1a30Sjl }
245825cf1a30Sjl 
245925cf1a30Sjl int
246025cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag)
246125cf1a30Sjl {
246225cf1a30Sjl 	/* stop memory patrol checking */
246325cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
2464*0cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
2465*0cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
2466*0cc8ae86Sav 		return (DDI_SUCCESS);
2467*0cc8ae86Sav 	}
2468*0cc8ae86Sav 
246925cf1a30Sjl 	if (mcp->mc_status & MC_POLL_RUNNING) {
247025cf1a30Sjl 		mcp->mc_status &= ~MC_POLL_RUNNING;
247125cf1a30Sjl 	}
247225cf1a30Sjl 	mcp->mc_status |= flag;
247325cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
247425cf1a30Sjl 
247525cf1a30Sjl 	return (DDI_SUCCESS);
247625cf1a30Sjl }
247725cf1a30Sjl 
247825cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */
247925cf1a30Sjl 
248025cf1a30Sjl int
248125cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag)
248225cf1a30Sjl {
248325cf1a30Sjl 	int i;
248425cf1a30Sjl 	uint64_t basepa;
248525cf1a30Sjl 
248625cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
2487*0cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
2488*0cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
2489*0cc8ae86Sav 		return (DDI_SUCCESS);
2490*0cc8ae86Sav 	}
249125cf1a30Sjl 	basepa = mcp->mc_start_address;
249225cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
249325cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
249425cf1a30Sjl 		return (DDI_FAILURE);
249525cf1a30Sjl 	}
249625cf1a30Sjl 
249725cf1a30Sjl 	if (basepa != mcp->mc_start_address) {
249825cf1a30Sjl 		if (mcp->mlist)
249925cf1a30Sjl 			mc_memlist_delete(mcp->mlist);
250025cf1a30Sjl 		mcp->mlist = NULL;
250125cf1a30Sjl 		mc_get_mlist(mcp);
250225cf1a30Sjl 	}
250325cf1a30Sjl 
250425cf1a30Sjl 	mcp->mc_status &= ~flag;
250525cf1a30Sjl 
250625cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
250725cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
250825cf1a30Sjl 		return (DDI_SUCCESS);
250925cf1a30Sjl 	}
251025cf1a30Sjl 
251125cf1a30Sjl 	if (!(mcp->mc_status & MC_POLL_RUNNING)) {
251225cf1a30Sjl 		/* restart memory patrol checking */
251325cf1a30Sjl 		mcp->mc_status |= MC_POLL_RUNNING;
251425cf1a30Sjl 		for (i = 0; i < BANKNUM_PER_SB; i++) {
251525cf1a30Sjl 			if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
251625cf1a30Sjl 				restart_patrol(mcp, i, NULL);
251725cf1a30Sjl 			}
251825cf1a30Sjl 		}
251925cf1a30Sjl 	}
252025cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
252125cf1a30Sjl 
252225cf1a30Sjl 	return (DDI_SUCCESS);
252325cf1a30Sjl }
252425cf1a30Sjl 
252525cf1a30Sjl static mc_opl_t *
252625cf1a30Sjl mc_pa_to_mcp(uint64_t pa)
252725cf1a30Sjl {
2528*0cc8ae86Sav 	mc_opl_t *mcp;
2529*0cc8ae86Sav 	int i;
2530*0cc8ae86Sav 
253125cf1a30Sjl 	ASSERT(MUTEX_HELD(&mcmutex));
2532*0cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
2533*0cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
2534*0cc8ae86Sav 			continue;
253525cf1a30Sjl 		/* if mac patrol is suspended, we cannot rely on it */
2536*0cc8ae86Sav 		if (!(mcp->mc_status & MC_POLL_RUNNING) ||
2537*0cc8ae86Sav 			(mcp->mc_status & MC_SOFT_SUSPENDED))
253825cf1a30Sjl 			continue;
2539*0cc8ae86Sav 		if ((mcp->mc_start_address <= pa) &&
2540*0cc8ae86Sav 			(pa < (mcp->mc_start_address + mcp->mc_size))) {
2541*0cc8ae86Sav 			return (mcp);
254225cf1a30Sjl 		}
254325cf1a30Sjl 	}
254425cf1a30Sjl 	return (NULL);
254525cf1a30Sjl }
254625cf1a30Sjl 
254725cf1a30Sjl /*
254825cf1a30Sjl  * Get Physical Board number from Logical one.
254925cf1a30Sjl  */
255025cf1a30Sjl static int
255125cf1a30Sjl mc_opl_get_physical_board(int sb)
255225cf1a30Sjl {
255325cf1a30Sjl 	if (&opl_get_physical_board) {
255425cf1a30Sjl 		return (opl_get_physical_board(sb));
255525cf1a30Sjl 	}
255625cf1a30Sjl 
255725cf1a30Sjl 	cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n");
255825cf1a30Sjl 	return (-1);
255925cf1a30Sjl }
256025cf1a30Sjl 
256125cf1a30Sjl /* ARGSUSED */
256225cf1a30Sjl int
256325cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen,
256425cf1a30Sjl 	int *lenp)
256525cf1a30Sjl {
2566*0cc8ae86Sav 	int i;
256725cf1a30Sjl 	int sb;
2568*0cc8ae86Sav 	int bank;
2569*0cc8ae86Sav 	mc_opl_t *mcp;
2570*0cc8ae86Sav 	char memb_num;
257125cf1a30Sjl 
257225cf1a30Sjl 	mutex_enter(&mcmutex);
257325cf1a30Sjl 
257425cf1a30Sjl 	if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) ||
257525cf1a30Sjl 		(!pa_is_valid(mcp, flt_addr))) {
257625cf1a30Sjl 		mutex_exit(&mcmutex);
257725cf1a30Sjl 		if (snprintf(buf, buflen, "UNKNOWN") >= buflen) {
257825cf1a30Sjl 			return (ENOSPC);
257925cf1a30Sjl 		} else {
258025cf1a30Sjl 			if (lenp)
258125cf1a30Sjl 				*lenp = strlen(buf);
258225cf1a30Sjl 		}
258325cf1a30Sjl 		return (0);
258425cf1a30Sjl 	}
258525cf1a30Sjl 
258625cf1a30Sjl 	bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address);
258725cf1a30Sjl 	sb = mc_opl_get_physical_board(mcp->mc_board_num);
258825cf1a30Sjl 
258925cf1a30Sjl 	if (sb == -1) {
259025cf1a30Sjl 		mutex_exit(&mcmutex);
259125cf1a30Sjl 		return (ENXIO);
259225cf1a30Sjl 	}
259325cf1a30Sjl 
2594*0cc8ae86Sav 	if (plat_model == MODEL_DC) {
2595*0cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
2596*0cc8ae86Sav 		snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s MEM%s MEM%s",
2597*0cc8ae86Sav 		    model_names[plat_model].unit_name, sb,
2598*0cc8ae86Sav 		    mc_dc_dimm_unum_table[i], mc_dc_dimm_unum_table[i + 1],
2599*0cc8ae86Sav 		    mc_dc_dimm_unum_table[i + 2], mc_dc_dimm_unum_table[i + 3]);
260025cf1a30Sjl 	} else {
2601*0cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
2602*0cc8ae86Sav 		memb_num = mc_ff_dimm_unum_table[i][0];
2603*0cc8ae86Sav 		snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s MEM%s MEM%s",
2604*0cc8ae86Sav 		    model_names[plat_model].unit_name,
2605*0cc8ae86Sav 		    model_names[plat_model].mem_name, memb_num,
2606*0cc8ae86Sav 		    &mc_ff_dimm_unum_table[i][1],
2607*0cc8ae86Sav 
2608*0cc8ae86Sav 		    &mc_ff_dimm_unum_table[i + 1][1],
2609*0cc8ae86Sav 		    &mc_ff_dimm_unum_table[i + 2][1],
2610*0cc8ae86Sav 		    &mc_ff_dimm_unum_table[i + 3][1]);
2611*0cc8ae86Sav 	}
2612*0cc8ae86Sav 	if (lenp) {
2613*0cc8ae86Sav 		*lenp = strlen(buf);
261425cf1a30Sjl 	}
261525cf1a30Sjl 	mutex_exit(&mcmutex);
261625cf1a30Sjl 	return (0);
261725cf1a30Sjl }
261825cf1a30Sjl 
261925cf1a30Sjl int
2620*0cc8ae86Sav opl_mc_suspend(void)
262125cf1a30Sjl {
262225cf1a30Sjl 	mc_opl_t *mcp;
2623*0cc8ae86Sav 	int i;
262425cf1a30Sjl 
262525cf1a30Sjl 	mutex_enter(&mcmutex);
2626*0cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
2627*0cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
2628*0cc8ae86Sav 			continue;
2629*0cc8ae86Sav 		mc_suspend(mcp, MC_SOFT_SUSPENDED);
263025cf1a30Sjl 	}
263125cf1a30Sjl 	mutex_exit(&mcmutex);
2632*0cc8ae86Sav 
263325cf1a30Sjl 	return (0);
263425cf1a30Sjl }
263525cf1a30Sjl 
263625cf1a30Sjl int
2637*0cc8ae86Sav opl_mc_resume(void)
263825cf1a30Sjl {
263925cf1a30Sjl 	mc_opl_t *mcp;
2640*0cc8ae86Sav 	int i;
264125cf1a30Sjl 
264225cf1a30Sjl 	mutex_enter(&mcmutex);
2643*0cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
2644*0cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
2645*0cc8ae86Sav 			continue;
2646*0cc8ae86Sav 		mc_resume(mcp, MC_SOFT_SUSPENDED);
264725cf1a30Sjl 	}
264825cf1a30Sjl 	mutex_exit(&mcmutex);
2649*0cc8ae86Sav 
265025cf1a30Sjl 	return (0);
265125cf1a30Sjl }
265225cf1a30Sjl static void
265325cf1a30Sjl insert_mcp(mc_opl_t *mcp)
265425cf1a30Sjl {
265525cf1a30Sjl 	mutex_enter(&mcmutex);
2656*0cc8ae86Sav 	if (mc_instances[mcp->mc_board_num] != NULL) {
2657*0cc8ae86Sav 		MC_LOG("mc-opl instance for board# %d already exists\n",
2658*0cc8ae86Sav 			mcp->mc_board_num);
2659*0cc8ae86Sav 	}
2660*0cc8ae86Sav 	mc_instances[mcp->mc_board_num] = mcp;
266125cf1a30Sjl 	mutex_exit(&mcmutex);
266225cf1a30Sjl }
266325cf1a30Sjl 
266425cf1a30Sjl static void
266525cf1a30Sjl delete_mcp(mc_opl_t *mcp)
266625cf1a30Sjl {
2667*0cc8ae86Sav 	mutex_enter(&mcmutex);
2668*0cc8ae86Sav 	mc_instances[mcp->mc_board_num] = 0;
2669*0cc8ae86Sav 	mutex_exit(&mcmutex);
267025cf1a30Sjl }
267125cf1a30Sjl 
267225cf1a30Sjl /* Error injection interface */
267325cf1a30Sjl 
267425cf1a30Sjl /* ARGSUSED */
267525cf1a30Sjl int
267625cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags)
267725cf1a30Sjl {
267825cf1a30Sjl 	mc_opl_t *mcp;
267925cf1a30Sjl 	int bank;
268025cf1a30Sjl 	uint32_t dimm_addr;
268125cf1a30Sjl 	uint32_t cntl;
268225cf1a30Sjl 	mc_addr_info_t maddr;
268325cf1a30Sjl 	uint32_t data, stat;
268425cf1a30Sjl 	int both_sides = 0;
268525cf1a30Sjl 	uint64_t pa0;
268625cf1a30Sjl 	on_trap_data_t otd;
268725cf1a30Sjl 	extern void cpu_flush_ecache(void);
268825cf1a30Sjl 
268925cf1a30Sjl 	MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags);
269025cf1a30Sjl 
269125cf1a30Sjl 	mutex_enter(&mcmutex);
269225cf1a30Sjl 	if ((mcp = mc_pa_to_mcp(pa)) == NULL) {
269325cf1a30Sjl 		mutex_exit(&mcmutex);
269425cf1a30Sjl 		MC_LOG("mc_inject_error: invalid pa\n");
269525cf1a30Sjl 		return (ENOTSUP);
269625cf1a30Sjl 	}
269725cf1a30Sjl 
269825cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
269925cf1a30Sjl 	mutex_exit(&mcmutex);
270025cf1a30Sjl 
270125cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
270225cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
270325cf1a30Sjl 		MC_LOG("mc-opl has been suspended.  No error injection.\n");
270425cf1a30Sjl 		return (EBUSY);
270525cf1a30Sjl 	}
270625cf1a30Sjl 
270725cf1a30Sjl 	/* convert pa to offset within the board */
270825cf1a30Sjl 	MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address);
270925cf1a30Sjl 
271025cf1a30Sjl 	if (!pa_is_valid(mcp, pa)) {
271125cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
271225cf1a30Sjl 		return (EINVAL);
271325cf1a30Sjl 	}
271425cf1a30Sjl 
271525cf1a30Sjl 	pa0 = pa - mcp->mc_start_address;
271625cf1a30Sjl 
271725cf1a30Sjl 	bank = pa_to_bank(mcp, pa0);
271825cf1a30Sjl 
271925cf1a30Sjl 	if (flags & MC_INJECT_FLAG_OTHER)
272025cf1a30Sjl 		bank = bank ^ 1;
272125cf1a30Sjl 
272225cf1a30Sjl 	if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) {
272325cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
272425cf1a30Sjl 		MC_LOG("Not mirror mode\n");
272525cf1a30Sjl 		return (EINVAL);
272625cf1a30Sjl 	}
272725cf1a30Sjl 
272825cf1a30Sjl 	dimm_addr = pa_to_dimm(mcp, pa0);
272925cf1a30Sjl 
273025cf1a30Sjl 	MC_LOG("injecting error to /LSB%d/B%d/D%x\n",
273125cf1a30Sjl 		mcp->mc_board_num, bank, dimm_addr);
273225cf1a30Sjl 
273325cf1a30Sjl 
273425cf1a30Sjl 	switch (error_type) {
273525cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
273625cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
273725cf1a30Sjl 	case MC_INJECT_MUE:
273825cf1a30Sjl 		both_sides = 1;
273925cf1a30Sjl 	}
274025cf1a30Sjl 
274125cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESET)
274225cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0);
274325cf1a30Sjl 
274425cf1a30Sjl 	ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK);
274525cf1a30Sjl 
274625cf1a30Sjl 	if (both_sides) {
274725cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0);
274825cf1a30Sjl 		ST_MAC_REG(MAC_EG_ADD(mcp, bank^1),
274925cf1a30Sjl 			dimm_addr & MAC_EG_ADD_MASK);
275025cf1a30Sjl 	}
275125cf1a30Sjl 
275225cf1a30Sjl 	switch (error_type) {
275325cf1a30Sjl 	case MC_INJECT_UE:
275425cf1a30Sjl 	case MC_INJECT_SUE:
275525cf1a30Sjl 	case MC_INJECT_MUE:
275625cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
275725cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
275825cf1a30Sjl 				|MAC_EG_FORCE_READ00|MAC_EG_FORCE_READ16
2759*0cc8ae86Sav 				|MAC_EG_RDERR_ONCE;
276025cf1a30Sjl 		} else {
276125cf1a30Sjl 			cntl = MAC_EG_ADD_FIX|MAC_EG_FORCE_DERR00
276225cf1a30Sjl 				|MAC_EG_FORCE_DERR16|MAC_EG_DERR_ONCE;
276325cf1a30Sjl 		}
276425cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
276525cf1a30Sjl 		break;
276625cf1a30Sjl 	case MC_INJECT_INTERMITTENT_CE:
276725cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
276825cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
276925cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
277025cf1a30Sjl 				|MAC_EG_FORCE_READ00
2771*0cc8ae86Sav 				|MAC_EG_RDERR_ONCE;
277225cf1a30Sjl 		} else {
277325cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
277425cf1a30Sjl 				|MAC_EG_FORCE_DERR16
277525cf1a30Sjl 				|MAC_EG_DERR_ONCE;
277625cf1a30Sjl 		}
277725cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
277825cf1a30Sjl 		break;
277925cf1a30Sjl 	case MC_INJECT_PERMANENT_CE:
278025cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
278125cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
278225cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
278325cf1a30Sjl 				|MAC_EG_FORCE_READ00
2784*0cc8ae86Sav 				|MAC_EG_RDERR_ALWAYS;
278525cf1a30Sjl 		} else {
278625cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
278725cf1a30Sjl 				|MAC_EG_FORCE_DERR16
278825cf1a30Sjl 				|MAC_EG_DERR_ALWAYS;
278925cf1a30Sjl 		}
279025cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
279125cf1a30Sjl 		break;
279225cf1a30Sjl 	case MC_INJECT_CMPE:
279325cf1a30Sjl 		data = 0xabcdefab;
279425cf1a30Sjl 		stphys(pa, data);
279525cf1a30Sjl 		cpu_flush_ecache();
279625cf1a30Sjl 		MC_LOG("CMPE: writing data %x to %lx\n", data, pa);
279725cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE);
279825cf1a30Sjl 		stphys(pa, data ^ 0xffffffff);
279925cf1a30Sjl 		cpu_flush_ecache();
280025cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), 0);
280125cf1a30Sjl 		MC_LOG("CMPE: write new data %xto %lx\n", data, pa);
280225cf1a30Sjl 		cntl = 0;
280325cf1a30Sjl 		break;
280425cf1a30Sjl 	case MC_INJECT_NOP:
280525cf1a30Sjl 		cntl = 0;
280625cf1a30Sjl 		break;
280725cf1a30Sjl 	default:
280825cf1a30Sjl 		MC_LOG("mc_inject_error: invalid option\n");
280925cf1a30Sjl 		cntl = 0;
281025cf1a30Sjl 	}
281125cf1a30Sjl 
281225cf1a30Sjl 	if (cntl) {
281325cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK);
281425cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
281525cf1a30Sjl 
281625cf1a30Sjl 		if (both_sides) {
281725cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
281825cf1a30Sjl 				MAC_EG_SETUP_MASK);
281925cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
282025cf1a30Sjl 		}
282125cf1a30Sjl 	}
282225cf1a30Sjl 
282325cf1a30Sjl 	/*
282425cf1a30Sjl 	 * For all injection cases except compare error, we
282525cf1a30Sjl 	 * must write to the PA to trigger the error.
282625cf1a30Sjl 	 */
282725cf1a30Sjl 
282825cf1a30Sjl 	if (flags & MC_INJECT_FLAG_ST) {
282925cf1a30Sjl 		data = 0xf0e0d0c0;
283025cf1a30Sjl 		MC_LOG("Writing %x to %lx\n", data, pa);
283125cf1a30Sjl 		stphys(pa, data);
283225cf1a30Sjl 		cpu_flush_ecache();
283325cf1a30Sjl 	}
283425cf1a30Sjl 
283525cf1a30Sjl 
283625cf1a30Sjl 	if (flags & MC_INJECT_FLAG_LD) {
283725cf1a30Sjl 		if (flags & MC_INJECT_FLAG_NO_TRAP) {
283825cf1a30Sjl 			if (on_trap(&otd, OT_DATA_EC)) {
283925cf1a30Sjl 				no_trap();
284025cf1a30Sjl 				MC_LOG("Trap occurred\n");
284125cf1a30Sjl 			} else {
284225cf1a30Sjl 				MC_LOG("On-trap Reading from %lx\n", pa);
284325cf1a30Sjl 				data = ldphys(pa);
284425cf1a30Sjl 				no_trap();
284525cf1a30Sjl 				MC_LOG("data = %x\n", data);
284625cf1a30Sjl 			}
284725cf1a30Sjl 		} else {
284825cf1a30Sjl 			MC_LOG("Reading from %lx\n", pa);
284925cf1a30Sjl 			data = ldphys(pa);
285025cf1a30Sjl 			MC_LOG("data = %x\n", data);
285125cf1a30Sjl 		}
285225cf1a30Sjl 	}
285325cf1a30Sjl 
285425cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESTART) {
285525cf1a30Sjl 		MC_LOG("Restart patrol\n");
285625cf1a30Sjl 		maddr.mi_maddr.ma_bd = mcp->mc_board_num;
285725cf1a30Sjl 		maddr.mi_maddr.ma_bank = bank;
285825cf1a30Sjl 		maddr.mi_maddr.ma_dimm_addr = dimm_addr;
285925cf1a30Sjl 		maddr.mi_valid = 1;
286025cf1a30Sjl 		maddr.mi_advance = 0;
286125cf1a30Sjl 		restart_patrol(mcp, bank, &maddr);
286225cf1a30Sjl 	}
286325cf1a30Sjl 
286425cf1a30Sjl 	if (flags & MC_INJECT_FLAG_POLL) {
2865*0cc8ae86Sav 		int running;
286625cf1a30Sjl 
286725cf1a30Sjl 		MC_LOG("Poll patrol error\n");
286825cf1a30Sjl 		stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank));
286925cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
2870*0cc8ae86Sav 		running = cntl & MAC_CNTL_PTRL_START;
287125cf1a30Sjl 		if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) {
2872*0cc8ae86Sav 			if (running) {
2873*0cc8ae86Sav 				/* speed up the scanning */
2874*0cc8ae86Sav 				mcp->mc_speedup_period[bank] = 2;
2875*0cc8ae86Sav 				MAC_CMD(mcp, bank, 0);
2876*0cc8ae86Sav 			} else {
2877*0cc8ae86Sav 				mcp->mc_speedup_period[bank] = 0;
2878*0cc8ae86Sav 				maddr.mi_valid = 0;
2879*0cc8ae86Sav 				maddr.mi_advance = 1;
2880*0cc8ae86Sav 				if (IS_MIRROR(mcp, bank))
2881*0cc8ae86Sav 					mc_error_handler_mir(mcp, bank,
2882*0cc8ae86Sav 						&maddr);
2883*0cc8ae86Sav 				else
2884*0cc8ae86Sav 					mc_error_handler(mcp, bank, &maddr);
288525cf1a30Sjl 
2886*0cc8ae86Sav 				restart_patrol(mcp, bank, &maddr);
2887*0cc8ae86Sav 			}
288825cf1a30Sjl 		} else
288925cf1a30Sjl 			restart_patrol(mcp, bank, NULL);
289025cf1a30Sjl 	}
289125cf1a30Sjl 
289225cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
289325cf1a30Sjl 	return (0);
289425cf1a30Sjl }
289525cf1a30Sjl void
289625cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data)
289725cf1a30Sjl {
2898*0cc8ae86Sav #ifndef lint
2899*0cc8ae86Sav 	uint32_t dummy;
2900*0cc8ae86Sav #endif
2901*0cc8ae86Sav 
290225cf1a30Sjl 	MC_LOG("0x%x -> pa(%lx)\n", data, pa);
290325cf1a30Sjl 	stphysio(pa, data);
2904*0cc8ae86Sav 
2905*0cc8ae86Sav 	/* force the above write to be processed by mac patrol */
2906*0cc8ae86Sav #ifndef lint
2907*0cc8ae86Sav 	dummy = ldphysio(pa);
2908*0cc8ae86Sav #endif
290925cf1a30Sjl }
291025cf1a30Sjl 
291125cf1a30Sjl uint32_t
291225cf1a30Sjl mc_ldphysio(uint64_t pa)
291325cf1a30Sjl {
291425cf1a30Sjl 	uint32_t rv;
291525cf1a30Sjl 
291625cf1a30Sjl 	rv = ldphysio(pa);
291725cf1a30Sjl 	MC_LOG("pa(%lx) = 0x%x\n", pa, rv);
291825cf1a30Sjl 	return (rv);
291925cf1a30Sjl }
2920*0cc8ae86Sav 
2921*0cc8ae86Sav #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
2922*0cc8ae86Sav 
2923*0cc8ae86Sav /*
2924*0cc8ae86Sav  * parse_unum_memory -- extract the board number and the DIMM name from
2925*0cc8ae86Sav  * the unum.
2926*0cc8ae86Sav  *
2927*0cc8ae86Sav  * Return 0 for success and non-zero for a failure.
2928*0cc8ae86Sav  */
2929*0cc8ae86Sav int
2930*0cc8ae86Sav parse_unum_memory(char *unum, int *board, char *dname)
2931*0cc8ae86Sav {
2932*0cc8ae86Sav 	char *c;
2933*0cc8ae86Sav 	char x, y, z;
2934*0cc8ae86Sav 
2935*0cc8ae86Sav 	if ((c = strstr(unum, "CMU")) != NULL) {
2936*0cc8ae86Sav 		/* DC Model */
2937*0cc8ae86Sav 		c += 3;
2938*0cc8ae86Sav 		*board = (uint8_t)stoi(&c);
2939*0cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
2940*0cc8ae86Sav 			return (1);
2941*0cc8ae86Sav 		}
2942*0cc8ae86Sav 		c += 3;
2943*0cc8ae86Sav 		if (strlen(c) < 3) {
2944*0cc8ae86Sav 			return (2);
2945*0cc8ae86Sav 		}
2946*0cc8ae86Sav 		if ((!isdigit(c[0])) || (!(isdigit(c[1]))) ||
2947*0cc8ae86Sav 		    ((c[2] != 'A') && (c[2] != 'B'))) {
2948*0cc8ae86Sav 			return (3);
2949*0cc8ae86Sav 		}
2950*0cc8ae86Sav 		x = c[0];
2951*0cc8ae86Sav 		y = c[1];
2952*0cc8ae86Sav 		z = c[2];
2953*0cc8ae86Sav 	} else if ((c = strstr(unum, "MBU_")) != NULL) {
2954*0cc8ae86Sav 		/*  FF1/FF2 Model */
2955*0cc8ae86Sav 		c += 4;
2956*0cc8ae86Sav 		if ((c[0] != 'A') && (c[0] != 'B')) {
2957*0cc8ae86Sav 			return (4);
2958*0cc8ae86Sav 		}
2959*0cc8ae86Sav 		if ((c = strstr(c, "MEMB")) == NULL) {
2960*0cc8ae86Sav 			return (5);
2961*0cc8ae86Sav 		}
2962*0cc8ae86Sav 		c += 4;
2963*0cc8ae86Sav 
2964*0cc8ae86Sav 		x = c[0];
2965*0cc8ae86Sav 		*board =  ((uint8_t)stoi(&c)) / 4;
2966*0cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
2967*0cc8ae86Sav 			return (6);
2968*0cc8ae86Sav 		}
2969*0cc8ae86Sav 		c += 3;
2970*0cc8ae86Sav 		if (strlen(c) < 2) {
2971*0cc8ae86Sav 			return (7);
2972*0cc8ae86Sav 		}
2973*0cc8ae86Sav 		if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) {
2974*0cc8ae86Sav 			return (8);
2975*0cc8ae86Sav 		}
2976*0cc8ae86Sav 		y = c[0];
2977*0cc8ae86Sav 		z = c[1];
2978*0cc8ae86Sav 	} else {
2979*0cc8ae86Sav 		return (9);
2980*0cc8ae86Sav 	}
2981*0cc8ae86Sav 	if (*board < 0) {
2982*0cc8ae86Sav 		return (10);
2983*0cc8ae86Sav 	}
2984*0cc8ae86Sav 	dname[0] = x;
2985*0cc8ae86Sav 	dname[1] = y;
2986*0cc8ae86Sav 	dname[2] = z;
2987*0cc8ae86Sav 	dname[3] = '\0';
2988*0cc8ae86Sav 	return (0);
2989*0cc8ae86Sav }
2990*0cc8ae86Sav 
2991*0cc8ae86Sav /*
2992*0cc8ae86Sav  * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and
2993*0cc8ae86Sav  * the DIMM name.
2994*0cc8ae86Sav  */
2995*0cc8ae86Sav int
2996*0cc8ae86Sav mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
2997*0cc8ae86Sav     int buflen, int *lenp)
2998*0cc8ae86Sav {
2999*0cc8ae86Sav 	int		ret = ENODEV;
3000*0cc8ae86Sav 	mc_dimm_info_t	*d = NULL;
3001*0cc8ae86Sav 
3002*0cc8ae86Sav 	if ((d = mcp->mc_dimm_list) == NULL)
3003*0cc8ae86Sav 		return (ENOTSUP);
3004*0cc8ae86Sav 
3005*0cc8ae86Sav 	for (; d != NULL; d = d->md_next) {
3006*0cc8ae86Sav 		if (strcmp(d->md_dimmname, dname) == 0) {
3007*0cc8ae86Sav 			break;
3008*0cc8ae86Sav 		}
3009*0cc8ae86Sav 	}
3010*0cc8ae86Sav 	if (d != NULL) {
3011*0cc8ae86Sav 		*lenp = strlen(d->md_serial) + strlen(d->md_partnum);
3012*0cc8ae86Sav 		if (buflen <=  *lenp) {
3013*0cc8ae86Sav 			cmn_err(CE_WARN, "mc_get_mem_sid_dimm: "
3014*0cc8ae86Sav 			    "buflen is smaller than %d\n", *lenp);
3015*0cc8ae86Sav 			ret = ENOSPC;
3016*0cc8ae86Sav 		} else {
3017*0cc8ae86Sav 			snprintf(buf, buflen, "%s:%s",
3018*0cc8ae86Sav 			    d->md_serial, d->md_partnum);
3019*0cc8ae86Sav 			ret = 0;
3020*0cc8ae86Sav 		}
3021*0cc8ae86Sav 	}
3022*0cc8ae86Sav 	MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n",
3023*0cc8ae86Sav 	    ret, dname, (ret == 0) ? buf : "");
3024*0cc8ae86Sav 	return (ret);
3025*0cc8ae86Sav }
3026*0cc8ae86Sav 
3027*0cc8ae86Sav int
3028*0cc8ae86Sav mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb,
3029*0cc8ae86Sav     int bank, uint32_t mf_type, uint32_t d_slot)
3030*0cc8ae86Sav {
3031*0cc8ae86Sav 	int	sb;
3032*0cc8ae86Sav 	int	lenp = buflen;
3033*0cc8ae86Sav 	int	id;
3034*0cc8ae86Sav 	int	ret;
3035*0cc8ae86Sav 	char	*dimmnm;
3036*0cc8ae86Sav 
3037*0cc8ae86Sav 	if ((sb = mc_opl_get_physical_board(lsb)) < 0) {
3038*0cc8ae86Sav 		return (ENODEV);
3039*0cc8ae86Sav 	}
3040*0cc8ae86Sav 
3041*0cc8ae86Sav 	if (mf_type == FLT_TYPE_PERMANENT_CE) {
3042*0cc8ae86Sav 		if (plat_model == MODEL_DC) {
3043*0cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
3044*0cc8ae86Sav 		} else {
3045*0cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
3046*0cc8ae86Sav 		}
3047*0cc8ae86Sav 		dimmnm = mc_dc_dimm_unum_table[id];
3048*0cc8ae86Sav 		if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen,
3049*0cc8ae86Sav 		    &lenp)) != 0) {
3050*0cc8ae86Sav 			return (ret);
3051*0cc8ae86Sav 		}
3052*0cc8ae86Sav 	} else {
3053*0cc8ae86Sav 		return (1);
3054*0cc8ae86Sav 	}
3055*0cc8ae86Sav 
3056*0cc8ae86Sav 	return (0);
3057*0cc8ae86Sav }
3058*0cc8ae86Sav 
3059*0cc8ae86Sav /*
3060*0cc8ae86Sav  * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum.
3061*0cc8ae86Sav  */
3062*0cc8ae86Sav int
3063*0cc8ae86Sav mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
3064*0cc8ae86Sav {
3065*0cc8ae86Sav 	int	i;
3066*0cc8ae86Sav 	int	ret = ENODEV;
3067*0cc8ae86Sav 	int	board;
3068*0cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
3069*0cc8ae86Sav 	mc_opl_t *mcp;
3070*0cc8ae86Sav 
3071*0cc8ae86Sav 	MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen);
3072*0cc8ae86Sav 	if ((ret = parse_unum_memory(unum, &board, dname)) != 0) {
3073*0cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
3074*0cc8ae86Sav 		    unum, ret);
3075*0cc8ae86Sav 		return (EINVAL);
3076*0cc8ae86Sav 	}
3077*0cc8ae86Sav 
3078*0cc8ae86Sav 	if (board < 0) {
3079*0cc8ae86Sav 		MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n",
3080*0cc8ae86Sav 		    board, dname);
3081*0cc8ae86Sav 		return (EINVAL);
3082*0cc8ae86Sav 	}
3083*0cc8ae86Sav 
3084*0cc8ae86Sav 	mutex_enter(&mcmutex);
3085*0cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
3086*0cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
3087*0cc8ae86Sav 			continue;
3088*0cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3089*0cc8ae86Sav 		if (mcp->mc_board_num == board) {
3090*0cc8ae86Sav 			ret = mc_get_mem_sid_dimm(mcp, dname, buf,
3091*0cc8ae86Sav 			    buflen, lenp);
3092*0cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
3093*0cc8ae86Sav 			break;
3094*0cc8ae86Sav 		}
3095*0cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
3096*0cc8ae86Sav 	}
3097*0cc8ae86Sav 	mutex_exit(&mcmutex);
3098*0cc8ae86Sav 	return (ret);
3099*0cc8ae86Sav }
3100*0cc8ae86Sav 
3101*0cc8ae86Sav /*
3102*0cc8ae86Sav  * mc_get_mem_offset -- get the offset in a DIMM for a given physical address.
3103*0cc8ae86Sav  */
3104*0cc8ae86Sav int
3105*0cc8ae86Sav mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
3106*0cc8ae86Sav {
3107*0cc8ae86Sav 	int		i;
3108*0cc8ae86Sav 	int		ret = ENODEV;
3109*0cc8ae86Sav 	mc_addr_t	maddr;
3110*0cc8ae86Sav 	mc_opl_t	*mcp;
3111*0cc8ae86Sav 
3112*0cc8ae86Sav 	mutex_enter(&mcmutex);
3113*0cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
3114*0cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
3115*0cc8ae86Sav 			continue;
3116*0cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3117*0cc8ae86Sav 		if (!pa_is_valid(mcp, paddr)) {
3118*0cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
3119*0cc8ae86Sav 			continue;
3120*0cc8ae86Sav 		}
3121*0cc8ae86Sav 		if (pa_to_maddr(mcp, paddr, &maddr) == 0) {
3122*0cc8ae86Sav 			*offp = maddr.ma_dimm_addr;
3123*0cc8ae86Sav 			ret = 0;
3124*0cc8ae86Sav 		}
3125*0cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
3126*0cc8ae86Sav 	}
3127*0cc8ae86Sav 	mutex_exit(&mcmutex);
3128*0cc8ae86Sav 	MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n",
3129*0cc8ae86Sav 	    ret, paddr, *offp);
3130*0cc8ae86Sav 	return (ret);
3131*0cc8ae86Sav }
3132*0cc8ae86Sav 
3133*0cc8ae86Sav /*
3134*0cc8ae86Sav  * dname_to_bankslot - Get the bank and slot number from the DIMM name.
3135*0cc8ae86Sav  */
3136*0cc8ae86Sav int
3137*0cc8ae86Sav dname_to_bankslot(char *dname, int *bank, int *slot)
3138*0cc8ae86Sav {
3139*0cc8ae86Sav 	int i;
3140*0cc8ae86Sav 	int tsz;
3141*0cc8ae86Sav 	char **tbl;
3142*0cc8ae86Sav 
3143*0cc8ae86Sav 	if (plat_model == MODEL_DC) { /* DC */
3144*0cc8ae86Sav 		tbl = mc_dc_dimm_unum_table;
3145*0cc8ae86Sav 		tsz = OPL_MAX_DIMMS;
3146*0cc8ae86Sav 	} else {
3147*0cc8ae86Sav 		tbl = mc_ff_dimm_unum_table;
3148*0cc8ae86Sav 		tsz = 2 * OPL_MAX_DIMMS;
3149*0cc8ae86Sav 	}
3150*0cc8ae86Sav 
3151*0cc8ae86Sav 	for (i = 0; i < tsz; i++) {
3152*0cc8ae86Sav 		if (strcmp(dname,  tbl[i]) == 0) {
3153*0cc8ae86Sav 			break;
3154*0cc8ae86Sav 		}
3155*0cc8ae86Sav 	}
3156*0cc8ae86Sav 	if (i == tsz) {
3157*0cc8ae86Sav 		return (1);
3158*0cc8ae86Sav 	}
3159*0cc8ae86Sav 	*bank = INDEX_TO_BANK(i);
3160*0cc8ae86Sav 	*slot = INDEX_TO_SLOT(i);
3161*0cc8ae86Sav 	return (0);
3162*0cc8ae86Sav }
3163*0cc8ae86Sav 
3164*0cc8ae86Sav /*
3165*0cc8ae86Sav  * mc_get_mem_addr -- get the physical address of a DIMM corresponding
3166*0cc8ae86Sav  * to the unum and sid.
3167*0cc8ae86Sav  */
3168*0cc8ae86Sav int
3169*0cc8ae86Sav mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr)
3170*0cc8ae86Sav {
3171*0cc8ae86Sav 	int	board;
3172*0cc8ae86Sav 	int	bank;
3173*0cc8ae86Sav 	int	slot;
3174*0cc8ae86Sav 	int	i;
3175*0cc8ae86Sav 	int	ret = ENODEV;
3176*0cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
3177*0cc8ae86Sav 	mc_addr_t maddr;
3178*0cc8ae86Sav 	mc_opl_t *mcp;
3179*0cc8ae86Sav 
3180*0cc8ae86Sav 	MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n",
3181*0cc8ae86Sav 	    unum, sid, offset);
3182*0cc8ae86Sav 	if (parse_unum_memory(unum, &board, dname) != 0) {
3183*0cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
3184*0cc8ae86Sav 		    unum, ret);
3185*0cc8ae86Sav 		return (EINVAL);
3186*0cc8ae86Sav 	}
3187*0cc8ae86Sav 
3188*0cc8ae86Sav 	if (board < 0) {
3189*0cc8ae86Sav 		MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n",
3190*0cc8ae86Sav 		    board, dname);
3191*0cc8ae86Sav 		return (EINVAL);
3192*0cc8ae86Sav 	}
3193*0cc8ae86Sav 
3194*0cc8ae86Sav 	mutex_enter(&mcmutex);
3195*0cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
3196*0cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
3197*0cc8ae86Sav 			continue;
3198*0cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3199*0cc8ae86Sav 		if (mcp->mc_board_num != board) {
3200*0cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
3201*0cc8ae86Sav 			continue;
3202*0cc8ae86Sav 		}
3203*0cc8ae86Sav 
3204*0cc8ae86Sav 		ret = dname_to_bankslot(dname, &bank, &slot);
3205*0cc8ae86Sav 		MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot);
3206*0cc8ae86Sav 		if (ret != 0) {
3207*0cc8ae86Sav 			MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n");
3208*0cc8ae86Sav 			ret = ENODEV;
3209*0cc8ae86Sav 		} else {
3210*0cc8ae86Sav 			maddr.ma_bd = board;
3211*0cc8ae86Sav 			maddr.ma_bank =  bank;
3212*0cc8ae86Sav 			maddr.ma_dimm_addr = offset;
3213*0cc8ae86Sav 			ret = mcaddr_to_pa(mcp, &maddr, paddr);
3214*0cc8ae86Sav 			if (ret != 0) {
3215*0cc8ae86Sav 				MC_LOG("mc_get_mem_addr: "
3216*0cc8ae86Sav 				    "mcaddr_to_pa failed\n");
3217*0cc8ae86Sav 				ret = ENODEV;
3218*0cc8ae86Sav 			}
3219*0cc8ae86Sav 		}
3220*0cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
3221*0cc8ae86Sav 	}
3222*0cc8ae86Sav 	mutex_exit(&mcmutex);
3223*0cc8ae86Sav 	MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr);
3224*0cc8ae86Sav 	return (ret);
3225*0cc8ae86Sav }
3226*0cc8ae86Sav 
3227*0cc8ae86Sav static void
3228*0cc8ae86Sav mc_free_dimm_list(mc_dimm_info_t *d)
3229*0cc8ae86Sav {
3230*0cc8ae86Sav 	mc_dimm_info_t *next;
3231*0cc8ae86Sav 
3232*0cc8ae86Sav 	while (d != NULL) {
3233*0cc8ae86Sav 		next = d->md_next;
3234*0cc8ae86Sav 		kmem_free(d, sizeof (mc_dimm_info_t));
3235*0cc8ae86Sav 		d = next;
3236*0cc8ae86Sav 	}
3237*0cc8ae86Sav }
3238*0cc8ae86Sav 
3239*0cc8ae86Sav /*
3240*0cc8ae86Sav  * mc_get_dimm_list -- get the list of dimms with serial-id info
3241*0cc8ae86Sav  * from the SP.
3242*0cc8ae86Sav  */
3243*0cc8ae86Sav mc_dimm_info_t *
3244*0cc8ae86Sav mc_get_dimm_list(mc_opl_t *mcp)
3245*0cc8ae86Sav {
3246*0cc8ae86Sav 	uint32_t	bufsz;
3247*0cc8ae86Sav 	uint32_t	maxbufsz;
3248*0cc8ae86Sav 	int		ret;
3249*0cc8ae86Sav 	int		sexp;
3250*0cc8ae86Sav 	board_dimm_info_t *bd_dimmp;
3251*0cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
3252*0cc8ae86Sav 
3253*0cc8ae86Sav 	maxbufsz = bufsz = sizeof (board_dimm_info_t) +
3254*0cc8ae86Sav 	    ((MCOPL_MAX_DIMMNAME +  MCOPL_MAX_SERIAL +
3255*0cc8ae86Sav 	    MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS);
3256*0cc8ae86Sav 
3257*0cc8ae86Sav 	bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP);
3258*0cc8ae86Sav 	ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz);
3259*0cc8ae86Sav 
3260*0cc8ae86Sav 	MC_LOG("mc_get_dimm_list:  scf_service_getinfo returned=%d\n", ret);
3261*0cc8ae86Sav 	if (ret == 0) {
3262*0cc8ae86Sav 		sexp = sizeof (board_dimm_info_t) +
3263*0cc8ae86Sav 		    ((bd_dimmp->bd_dnamesz +  bd_dimmp->bd_serialsz +
3264*0cc8ae86Sav 		    bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms);
3265*0cc8ae86Sav 
3266*0cc8ae86Sav 		if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) &&
3267*0cc8ae86Sav 		    (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) &&
3268*0cc8ae86Sav 		    (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) &&
3269*0cc8ae86Sav 		    (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) &&
3270*0cc8ae86Sav 		    (sexp <= bufsz)) {
3271*0cc8ae86Sav 
3272*0cc8ae86Sav #ifdef DEBUG
3273*0cc8ae86Sav 			if (oplmc_debug)
3274*0cc8ae86Sav 				mc_dump_dimm_info(bd_dimmp);
3275*0cc8ae86Sav #endif
3276*0cc8ae86Sav 			dimm_list = mc_prepare_dimmlist(bd_dimmp);
3277*0cc8ae86Sav 
3278*0cc8ae86Sav 		} else {
3279*0cc8ae86Sav 			cmn_err(CE_WARN, "DIMM info version mismatch\n");
3280*0cc8ae86Sav 		}
3281*0cc8ae86Sav 	}
3282*0cc8ae86Sav 	kmem_free(bd_dimmp, maxbufsz);
3283*0cc8ae86Sav 	MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list);
3284*0cc8ae86Sav 	return (dimm_list);
3285*0cc8ae86Sav }
3286*0cc8ae86Sav 
3287*0cc8ae86Sav /*
3288*0cc8ae86Sav  * mc_prepare_dimmlist - Prepare the dimm list from the infomation
3289*0cc8ae86Sav  * recieved from the SP.
3290*0cc8ae86Sav  */
3291*0cc8ae86Sav mc_dimm_info_t *
3292*0cc8ae86Sav mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp)
3293*0cc8ae86Sav {
3294*0cc8ae86Sav 	char	*dimm_name;
3295*0cc8ae86Sav 	char	*serial;
3296*0cc8ae86Sav 	char	*part;
3297*0cc8ae86Sav 	int	dimm;
3298*0cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
3299*0cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
3300*0cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
3301*0cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
3302*0cc8ae86Sav 	mc_dimm_info_t	*d;
3303*0cc8ae86Sav 
3304*0cc8ae86Sav 	dimm_name = (char *)(bd_dimmp + 1);
3305*0cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
3306*0cc8ae86Sav 
3307*0cc8ae86Sav 		d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t),
3308*0cc8ae86Sav 		    KM_SLEEP);
3309*0cc8ae86Sav 		snprintf(d->md_dimmname, dnamesz + 1, "%s", dimm_name);
3310*0cc8ae86Sav 		serial = dimm_name + dnamesz;
3311*0cc8ae86Sav 		snprintf(d->md_serial, sersz + 1, "%s", serial);
3312*0cc8ae86Sav 		part = serial + sersz;
3313*0cc8ae86Sav 		snprintf(d->md_partnum, partsz + 1, "%s", part);
3314*0cc8ae86Sav 
3315*0cc8ae86Sav 		d->md_next = dimm_list;
3316*0cc8ae86Sav 		dimm_list = d;
3317*0cc8ae86Sav 		dimm_name = part + partsz;
3318*0cc8ae86Sav 	}
3319*0cc8ae86Sav 	return (dimm_list);
3320*0cc8ae86Sav }
3321*0cc8ae86Sav 
3322*0cc8ae86Sav #ifdef DEBUG
3323*0cc8ae86Sav void
3324*0cc8ae86Sav mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz)
3325*0cc8ae86Sav {
3326*0cc8ae86Sav 	char dname[MCOPL_MAX_DIMMNAME + 1];
3327*0cc8ae86Sav 	char serial[MCOPL_MAX_SERIAL + 1];
3328*0cc8ae86Sav 	char part[ MCOPL_MAX_PARTNUM + 1];
3329*0cc8ae86Sav 	char *b;
3330*0cc8ae86Sav 
3331*0cc8ae86Sav 	b = buf;
3332*0cc8ae86Sav 	snprintf(dname, dnamesz + 1, "%s", b);
3333*0cc8ae86Sav 	b += dnamesz;
3334*0cc8ae86Sav 	snprintf(serial, serialsz + 1, "%s", b);
3335*0cc8ae86Sav 	b += serialsz;
3336*0cc8ae86Sav 	snprintf(part, partnumsz + 1, "%s", b);
3337*0cc8ae86Sav 	printf("DIMM=%s  Serial=%s PartNum=%s\n", dname, serial, part);
3338*0cc8ae86Sav }
3339*0cc8ae86Sav 
3340*0cc8ae86Sav void
3341*0cc8ae86Sav mc_dump_dimm_info(board_dimm_info_t *bd_dimmp)
3342*0cc8ae86Sav {
3343*0cc8ae86Sav 	int	dimm;
3344*0cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
3345*0cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
3346*0cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
3347*0cc8ae86Sav 	char	*buf;
3348*0cc8ae86Sav 
3349*0cc8ae86Sav 	printf("Version=%d Board=%02d DIMMs=%d NameSize=%d "
3350*0cc8ae86Sav 	    "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version,
3351*0cc8ae86Sav 	    bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz,
3352*0cc8ae86Sav 	    bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz);
3353*0cc8ae86Sav 	printf("======================================================\n");
3354*0cc8ae86Sav 
3355*0cc8ae86Sav 	buf = (char *)(bd_dimmp + 1);
3356*0cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
3357*0cc8ae86Sav 		mc_dump_dimm(buf, dnamesz, sersz, partsz);
3358*0cc8ae86Sav 		buf += dnamesz + sersz + partsz;
3359*0cc8ae86Sav 	}
3360*0cc8ae86Sav 	printf("======================================================\n");
3361*0cc8ae86Sav }
3362*0cc8ae86Sav 
3363*0cc8ae86Sav 
3364*0cc8ae86Sav /* ARGSUSED */
3365*0cc8ae86Sav static int
3366*0cc8ae86Sav mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
3367*0cc8ae86Sav 	int *rvalp)
3368*0cc8ae86Sav {
3369*0cc8ae86Sav 	caddr_t	buf;
3370*0cc8ae86Sav 	uint64_t pa;
3371*0cc8ae86Sav 	int rv = 0;
3372*0cc8ae86Sav 	int i;
3373*0cc8ae86Sav 	uint32_t flags;
3374*0cc8ae86Sav 	static uint32_t offset = 0;
3375*0cc8ae86Sav 
3376*0cc8ae86Sav 
3377*0cc8ae86Sav 	flags = (cmd >> 4) & 0xfffffff;
3378*0cc8ae86Sav 
3379*0cc8ae86Sav 	cmd &= 0xf;
3380*0cc8ae86Sav 
3381*0cc8ae86Sav 	MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags);
3382*0cc8ae86Sav 
3383*0cc8ae86Sav 	if (arg != NULL) {
3384*0cc8ae86Sav 		if (ddi_copyin((const void *)arg, (void *)&pa,
3385*0cc8ae86Sav 			sizeof (uint64_t), 0) < 0) {
3386*0cc8ae86Sav 			rv = EFAULT;
3387*0cc8ae86Sav 			return (rv);
3388*0cc8ae86Sav 		}
3389*0cc8ae86Sav 		buf = NULL;
3390*0cc8ae86Sav 	} else {
3391*0cc8ae86Sav 		buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP);
3392*0cc8ae86Sav 
3393*0cc8ae86Sav 		pa = va_to_pa(buf);
3394*0cc8ae86Sav 		pa += offset;
3395*0cc8ae86Sav 
3396*0cc8ae86Sav 		offset += 64;
3397*0cc8ae86Sav 		if (offset >= PAGESIZE)
3398*0cc8ae86Sav 			offset = 0;
3399*0cc8ae86Sav 	}
3400*0cc8ae86Sav 
3401*0cc8ae86Sav 	switch (cmd) {
3402*0cc8ae86Sav 	case MCI_CE:
3403*0cc8ae86Sav 		mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa,
3404*0cc8ae86Sav 			flags);
3405*0cc8ae86Sav 		break;
3406*0cc8ae86Sav 	case MCI_PERM_CE:
3407*0cc8ae86Sav 		mc_inject_error(MC_INJECT_PERMANENT_CE, pa,
3408*0cc8ae86Sav 			flags);
3409*0cc8ae86Sav 		break;
3410*0cc8ae86Sav 	case MCI_UE:
3411*0cc8ae86Sav 		mc_inject_error(MC_INJECT_UE, pa,
3412*0cc8ae86Sav 			flags);
3413*0cc8ae86Sav 		break;
3414*0cc8ae86Sav 	case MCI_M_CE:
3415*0cc8ae86Sav 		mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa,
3416*0cc8ae86Sav 			flags);
3417*0cc8ae86Sav 		break;
3418*0cc8ae86Sav 	case MCI_M_PCE:
3419*0cc8ae86Sav 		mc_inject_error(MC_INJECT_PERMANENT_MCE, pa,
3420*0cc8ae86Sav 			flags);
3421*0cc8ae86Sav 		break;
3422*0cc8ae86Sav 	case MCI_M_UE:
3423*0cc8ae86Sav 		mc_inject_error(MC_INJECT_MUE, pa,
3424*0cc8ae86Sav 			flags);
3425*0cc8ae86Sav 		break;
3426*0cc8ae86Sav 	case MCI_CMP:
3427*0cc8ae86Sav 		mc_inject_error(MC_INJECT_CMPE, pa,
3428*0cc8ae86Sav 			flags);
3429*0cc8ae86Sav 		break;
3430*0cc8ae86Sav 	case MCI_NOP:
3431*0cc8ae86Sav 		mc_inject_error(MC_INJECT_NOP, pa, flags);
3432*0cc8ae86Sav 		break;
3433*0cc8ae86Sav 	case MCI_SHOW_ALL:
3434*0cc8ae86Sav 		mc_debug_show_all = 1;
3435*0cc8ae86Sav 		break;
3436*0cc8ae86Sav 	case MCI_SHOW_NONE:
3437*0cc8ae86Sav 		mc_debug_show_all = 0;
3438*0cc8ae86Sav 		break;
3439*0cc8ae86Sav 	case MCI_ALLOC:
3440*0cc8ae86Sav 		/*
3441*0cc8ae86Sav 		 * just allocate some kernel memory and never free it
3442*0cc8ae86Sav 		 * 512 MB seems to be the maximum size supported.
3443*0cc8ae86Sav 		 */
3444*0cc8ae86Sav 		cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512);
3445*0cc8ae86Sav 		for (i = 0; i < flags; i++) {
3446*0cc8ae86Sav 			buf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP);
3447*0cc8ae86Sav 			cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n",
3448*0cc8ae86Sav 				(u_longlong_t)buf, (u_longlong_t)va_to_pa(buf));
3449*0cc8ae86Sav 		}
3450*0cc8ae86Sav 		break;
3451*0cc8ae86Sav 	case MCI_SUSPEND:
3452*0cc8ae86Sav 		(void) opl_mc_suspend();
3453*0cc8ae86Sav 		break;
3454*0cc8ae86Sav 	case MCI_RESUME:
3455*0cc8ae86Sav 		(void) opl_mc_resume();
3456*0cc8ae86Sav 		break;
3457*0cc8ae86Sav 	default:
3458*0cc8ae86Sav 		rv = ENXIO;
3459*0cc8ae86Sav 	}
3460*0cc8ae86Sav 	return (rv);
3461*0cc8ae86Sav }
3462*0cc8ae86Sav 
3463*0cc8ae86Sav #endif /* DEBUG */
3464