xref: /illumos-gate/usr/src/uts/sun4u/opl/io/mc-opl.c (revision 601c2e1e)
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  */
211e2e7a75Shuah /*
2268ac2337Sjl  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
231e2e7a75Shuah  * Use is subject to license terms.
241e2e7a75Shuah  */
2525cf1a30Sjl /*
26*601c2e1eSdhain  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2007
2725cf1a30Sjl  */
2825cf1a30Sjl 
2925cf1a30Sjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
3025cf1a30Sjl 
3125cf1a30Sjl #include <sys/types.h>
3225cf1a30Sjl #include <sys/sysmacros.h>
3325cf1a30Sjl #include <sys/conf.h>
3425cf1a30Sjl #include <sys/modctl.h>
3525cf1a30Sjl #include <sys/stat.h>
3625cf1a30Sjl #include <sys/async.h>
371e2e7a75Shuah #include <sys/machcpuvar.h>
3825cf1a30Sjl #include <sys/machsystm.h>
390cc8ae86Sav #include <sys/promif.h>
4025cf1a30Sjl #include <sys/ksynch.h>
4125cf1a30Sjl #include <sys/ddi.h>
4225cf1a30Sjl #include <sys/sunddi.h>
43d8a0cca9Swh #include <sys/sunndi.h>
4425cf1a30Sjl #include <sys/ddifm.h>
4525cf1a30Sjl #include <sys/fm/protocol.h>
4625cf1a30Sjl #include <sys/fm/util.h>
4725cf1a30Sjl #include <sys/kmem.h>
4825cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h>
4925cf1a30Sjl #include <sys/memlist.h>
5025cf1a30Sjl #include <sys/param.h>
510cc8ae86Sav #include <sys/disp.h>
5225cf1a30Sjl #include <vm/page.h>
5325cf1a30Sjl #include <sys/mc-opl.h>
540cc8ae86Sav #include <sys/opl.h>
550cc8ae86Sav #include <sys/opl_dimm.h>
560cc8ae86Sav #include <sys/scfd/scfostoescf.h>
57cfb9e062Shyw #include <sys/cpu_module.h>
58cfb9e062Shyw #include <vm/seg_kmem.h>
59cfb9e062Shyw #include <sys/vmem.h>
60cfb9e062Shyw #include <vm/hat_sfmmu.h>
61cfb9e062Shyw #include <sys/vmsystm.h>
62738dd194Shyw #include <sys/membar.h>
6325cf1a30Sjl 
6425cf1a30Sjl /*
6525cf1a30Sjl  * Function prototypes
6625cf1a30Sjl  */
6725cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *);
6825cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *);
6925cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
7025cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
7125cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
7225cf1a30Sjl 
730cc8ae86Sav static int mc_poll_init(void);
740cc8ae86Sav static void mc_poll_fini(void);
7525cf1a30Sjl static int mc_board_add(mc_opl_t *mcp);
7625cf1a30Sjl static int mc_board_del(mc_opl_t *mcp);
7725cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag);
7825cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag);
790cc8ae86Sav int opl_mc_suspend(void);
800cc8ae86Sav int opl_mc_resume(void);
8125cf1a30Sjl 
8225cf1a30Sjl static void insert_mcp(mc_opl_t *mcp);
8325cf1a30Sjl static void delete_mcp(mc_opl_t *mcp);
8425cf1a30Sjl 
8525cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr);
8625cf1a30Sjl 
87738dd194Shyw static int mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa);
8825cf1a30Sjl 
8925cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *);
900cc8ae86Sav int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr);
910cc8ae86Sav int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
920cc8ae86Sav int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp);
930cc8ae86Sav int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
940cc8ae86Sav     int buflen, int *lenp);
950cc8ae86Sav mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp);
960cc8ae86Sav mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp);
970cc8ae86Sav int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank,
980cc8ae86Sav     uint32_t mf_type, uint32_t d_slot);
990cc8ae86Sav static void mc_free_dimm_list(mc_dimm_info_t *d);
10025cf1a30Sjl static void mc_get_mlist(mc_opl_t *);
1010cc8ae86Sav static void mc_polling(void);
1020cc8ae86Sav static int mc_opl_get_physical_board(int);
1030cc8ae86Sav 
104*601c2e1eSdhain static void mc_clear_rewrite(mc_opl_t *mcp, int i);
105*601c2e1eSdhain static void mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state);
106*601c2e1eSdhain 
1070cc8ae86Sav #ifdef	DEBUG
1080cc8ae86Sav static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *);
1090cc8ae86Sav void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz);
1100cc8ae86Sav void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp);
1110cc8ae86Sav #endif
11225cf1a30Sjl 
11325cf1a30Sjl #pragma weak opl_get_physical_board
11425cf1a30Sjl extern int opl_get_physical_board(int);
1150cc8ae86Sav extern int plat_max_boards(void);
11625cf1a30Sjl 
11725cf1a30Sjl /*
11825cf1a30Sjl  * Configuration data structures
11925cf1a30Sjl  */
12025cf1a30Sjl static struct cb_ops mc_cb_ops = {
12125cf1a30Sjl 	mc_open,			/* open */
12225cf1a30Sjl 	mc_close,			/* close */
12325cf1a30Sjl 	nulldev,			/* strategy */
12425cf1a30Sjl 	nulldev,			/* print */
12525cf1a30Sjl 	nodev,				/* dump */
12625cf1a30Sjl 	nulldev,			/* read */
12725cf1a30Sjl 	nulldev,			/* write */
12825cf1a30Sjl 	mc_ioctl,			/* ioctl */
12925cf1a30Sjl 	nodev,				/* devmap */
13025cf1a30Sjl 	nodev,				/* mmap */
13125cf1a30Sjl 	nodev,				/* segmap */
13225cf1a30Sjl 	nochpoll,			/* poll */
13325cf1a30Sjl 	ddi_prop_op,			/* cb_prop_op */
13425cf1a30Sjl 	0,				/* streamtab */
13525cf1a30Sjl 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
13625cf1a30Sjl 	CB_REV,				/* rev */
13725cf1a30Sjl 	nodev,				/* cb_aread */
13825cf1a30Sjl 	nodev				/* cb_awrite */
13925cf1a30Sjl };
14025cf1a30Sjl 
14125cf1a30Sjl static struct dev_ops mc_ops = {
14225cf1a30Sjl 	DEVO_REV,			/* rev */
14325cf1a30Sjl 	0,				/* refcnt  */
14425cf1a30Sjl 	ddi_getinfo_1to1,		/* getinfo */
14525cf1a30Sjl 	nulldev,			/* identify */
14625cf1a30Sjl 	nulldev,			/* probe */
14725cf1a30Sjl 	mc_attach,			/* attach */
14825cf1a30Sjl 	mc_detach,			/* detach */
14925cf1a30Sjl 	nulldev,			/* reset */
15025cf1a30Sjl 	&mc_cb_ops,			/* cb_ops */
15125cf1a30Sjl 	(struct bus_ops *)0,		/* bus_ops */
15225cf1a30Sjl 	nulldev				/* power */
15325cf1a30Sjl };
15425cf1a30Sjl 
15525cf1a30Sjl /*
15625cf1a30Sjl  * Driver globals
15725cf1a30Sjl  */
15825cf1a30Sjl 
1590cc8ae86Sav static enum {
1600cc8ae86Sav 	MODEL_FF1 = 0,
1610cc8ae86Sav 	MODEL_FF2 = 1,
1620cc8ae86Sav 	MODEL_DC = 2
1630cc8ae86Sav } plat_model = MODEL_DC;	/* The default behaviour is DC */
1640cc8ae86Sav 
1650cc8ae86Sav static struct plat_model_names {
1660cc8ae86Sav 	const char *unit_name;
1670cc8ae86Sav 	const char *mem_name;
1680cc8ae86Sav } model_names[] = {
1690cc8ae86Sav 	{ "MBU_A", "MEMB" },
1700cc8ae86Sav 	{ "MBU_B", "MEMB" },
1710cc8ae86Sav 	{ "CMU", "" }
1720cc8ae86Sav };
17325cf1a30Sjl 
1740cc8ae86Sav /*
1750cc8ae86Sav  * The DIMM Names for DC platform.
1760cc8ae86Sav  * The index into this table is made up of (bank, dslot),
1770cc8ae86Sav  * Where dslot occupies bits 0-1 and bank occupies 2-4.
1780cc8ae86Sav  */
1790cc8ae86Sav static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = {
1800cc8ae86Sav 	/* --------CMUnn----------- */
1810cc8ae86Sav 	/* --CS0-----|--CS1------ */
1820cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
183c964b0e6Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
184c964b0e6Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */
185c964b0e6Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */
186c964b0e6Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */
187c964b0e6Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */
188c964b0e6Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */
189c964b0e6Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */
190c964b0e6Sraghuram 	"31A", "30A", "31B", "30B"  /* Bank 7 (MAC 3 bank 1) */
1910cc8ae86Sav };
1920cc8ae86Sav 
1930cc8ae86Sav /*
1940cc8ae86Sav  * The DIMM Names for FF1/FF2 platforms.
1950cc8ae86Sav  * The index into this table is made up of (board, bank, dslot),
1960cc8ae86Sav  * Where dslot occupies bits 0-1, bank occupies 2-4 and
1970cc8ae86Sav  * board occupies the bit 5.
1980cc8ae86Sav  */
1990cc8ae86Sav static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = {
2000cc8ae86Sav 	/* --------CMU0---------- */
2010cc8ae86Sav 	/* --CS0-----|--CS1------ */
2020cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
203c964b0e6Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
204c964b0e6Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */
205c964b0e6Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */
206c964b0e6Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */
207c964b0e6Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */
208c964b0e6Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */
209c964b0e6Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */
210c964b0e6Sraghuram 	"31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */
2110cc8ae86Sav 	/* --------CMU1---------- */
2120cc8ae86Sav 	/* --CS0-----|--CS1------ */
2130cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
214c964b0e6Sraghuram 	"43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */
215c964b0e6Sraghuram 	"41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */
216c964b0e6Sraghuram 	"53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */
217c964b0e6Sraghuram 	"51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */
218c964b0e6Sraghuram 	"63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */
219c964b0e6Sraghuram 	"61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */
220c964b0e6Sraghuram 	"73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */
221c964b0e6Sraghuram 	"71A", "70A", "71B", "70B"  /* Bank 7 (MAC 3 bank 1) */
2220cc8ae86Sav };
2230cc8ae86Sav 
2240cc8ae86Sav #define	BD_BK_SLOT_TO_INDEX(bd, bk, s)			\
2250cc8ae86Sav 	(((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03))
2260cc8ae86Sav 
2270cc8ae86Sav #define	INDEX_TO_BANK(i)			(((i) & 0x1C) >> 2)
2280cc8ae86Sav #define	INDEX_TO_SLOT(i)			((i) & 0x03)
2290cc8ae86Sav 
230aeb241b2Sav #define	SLOT_TO_CS(slot)	((slot & 0x3) >> 1)
231aeb241b2Sav 
2320cc8ae86Sav /* Isolation unit size is 64 MB */
2330cc8ae86Sav #define	MC_ISOLATION_BSIZE	(64 * 1024 * 1024)
2340cc8ae86Sav 
2350cc8ae86Sav #define	MC_MAX_SPEEDS 7
2360cc8ae86Sav 
2370cc8ae86Sav typedef struct {
2380cc8ae86Sav 	uint32_t mc_speeds;
2390cc8ae86Sav 	uint32_t mc_period;
2400cc8ae86Sav } mc_scan_speed_t;
2410cc8ae86Sav 
2420cc8ae86Sav #define	MC_CNTL_SPEED_SHIFT 26
2430cc8ae86Sav 
24437afe445Shyw /*
24537afe445Shyw  * In mirror mode, we normalized the bank idx to "even" since
24637afe445Shyw  * the HW treats them as one unit w.r.t programming.
24737afe445Shyw  * This bank index will be the "effective" bank index.
24837afe445Shyw  * All mirrored bank state info on mc_period, mc_speedup_period
24937afe445Shyw  * will be stored in the even bank structure to avoid code duplication.
25037afe445Shyw  */
25137afe445Shyw #define	MIRROR_IDX(bankidx)	(bankidx & ~1)
25237afe445Shyw 
2530cc8ae86Sav static mc_scan_speed_t	mc_scan_speeds[MC_MAX_SPEEDS] = {
2540cc8ae86Sav 	{0x6 << MC_CNTL_SPEED_SHIFT, 0},
2550cc8ae86Sav 	{0x5 << MC_CNTL_SPEED_SHIFT, 32},
2560cc8ae86Sav 	{0x4 << MC_CNTL_SPEED_SHIFT, 64},
2570cc8ae86Sav 	{0x3 << MC_CNTL_SPEED_SHIFT, 128},
2580cc8ae86Sav 	{0x2 << MC_CNTL_SPEED_SHIFT, 256},
2590cc8ae86Sav 	{0x1 << MC_CNTL_SPEED_SHIFT, 512},
2600cc8ae86Sav 	{0x0 << MC_CNTL_SPEED_SHIFT, 1024}
2610cc8ae86Sav };
2620cc8ae86Sav 
2630cc8ae86Sav static uint32_t	mc_max_speed = (0x6 << 26);
2640cc8ae86Sav 
2650cc8ae86Sav int mc_isolation_bsize = MC_ISOLATION_BSIZE;
2660cc8ae86Sav int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC;
2670cc8ae86Sav int mc_max_scf_retry = 16;
2680cc8ae86Sav int mc_max_scf_logs = 64;
2690cc8ae86Sav int mc_max_errlog_processed = BANKNUM_PER_SB*2;
2700cc8ae86Sav int mc_scan_period = 12 * 60 * 60;	/* 12 hours period */
2710cc8ae86Sav int mc_max_rewrite_loop = 100;
2720cc8ae86Sav int mc_rewrite_delay = 10;
2730cc8ae86Sav /*
2740cc8ae86Sav  * it takes SCF about 300 m.s. to process a requst.  We can bail out
2750cc8ae86Sav  * if it is busy.  It does not pay to wait for it too long.
2760cc8ae86Sav  */
2770cc8ae86Sav int mc_max_scf_loop = 2;
2780cc8ae86Sav int mc_scf_delay = 100;
2790cc8ae86Sav int mc_pce_dropped = 0;
2800cc8ae86Sav int mc_poll_priority = MINCLSYSPRI;
281*601c2e1eSdhain int mc_max_rewrite_retry = 6 * 60;
28225cf1a30Sjl 
2830cc8ae86Sav 
2840cc8ae86Sav /*
2851039f409Sav  * Mutex hierarchy in mc-opl
2860cc8ae86Sav  * If both mcmutex and mc_lock must be held,
2870cc8ae86Sav  * mcmutex must be acquired first, and then mc_lock.
2880cc8ae86Sav  */
2890cc8ae86Sav 
2900cc8ae86Sav static kmutex_t mcmutex;
2910cc8ae86Sav mc_opl_t *mc_instances[OPL_MAX_BOARDS];
2920cc8ae86Sav 
2930cc8ae86Sav static kmutex_t mc_polling_lock;
2940cc8ae86Sav static kcondvar_t mc_polling_cv;
2950cc8ae86Sav static kcondvar_t mc_poll_exit_cv;
2960cc8ae86Sav static int mc_poll_cmd = 0;
2970cc8ae86Sav static int mc_pollthr_running = 0;
2980cc8ae86Sav int mc_timeout_period = 0; /* this is in m.s. */
29925cf1a30Sjl void *mc_statep;
30025cf1a30Sjl 
30125cf1a30Sjl #ifdef	DEBUG
3022742aa22Shyw int oplmc_debug = 0;
30325cf1a30Sjl #endif
30425cf1a30Sjl 
3050cc8ae86Sav static int mc_debug_show_all = 0;
30625cf1a30Sjl 
30725cf1a30Sjl extern struct mod_ops mod_driverops;
30825cf1a30Sjl 
30925cf1a30Sjl static struct modldrv modldrv = {
31025cf1a30Sjl 	&mod_driverops,			/* module type, this one is a driver */
3110cc8ae86Sav 	"OPL Memory-controller %I%",	/* module name */
31225cf1a30Sjl 	&mc_ops,			/* driver ops */
31325cf1a30Sjl };
31425cf1a30Sjl 
31525cf1a30Sjl static struct modlinkage modlinkage = {
31625cf1a30Sjl 	MODREV_1,		/* rev */
31725cf1a30Sjl 	(void *)&modldrv,
31825cf1a30Sjl 	NULL
31925cf1a30Sjl };
32025cf1a30Sjl 
32125cf1a30Sjl #pragma weak opl_get_mem_unum
3220cc8ae86Sav #pragma weak opl_get_mem_sid
3230cc8ae86Sav #pragma weak opl_get_mem_offset
3240cc8ae86Sav #pragma weak opl_get_mem_addr
3250cc8ae86Sav 
32625cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *);
3270cc8ae86Sav extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp);
3280cc8ae86Sav extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp);
3290cc8ae86Sav extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset,
3300cc8ae86Sav     uint64_t *paddr);
3310cc8ae86Sav 
33225cf1a30Sjl 
33325cf1a30Sjl /*
33425cf1a30Sjl  * pseudo-mc node portid format
33525cf1a30Sjl  *
33625cf1a30Sjl  *		[10]   = 0
33725cf1a30Sjl  *		[9]    = 1
33825cf1a30Sjl  *		[8]    = LSB_ID[4] = 0
33925cf1a30Sjl  *		[7:4]  = LSB_ID[3:0]
34025cf1a30Sjl  *		[3:0]  = 0
34125cf1a30Sjl  *
34225cf1a30Sjl  */
34325cf1a30Sjl 
34425cf1a30Sjl /*
34525cf1a30Sjl  * These are the module initialization routines.
34625cf1a30Sjl  */
34725cf1a30Sjl int
34825cf1a30Sjl _init(void)
34925cf1a30Sjl {
3500cc8ae86Sav 	int	error;
3510cc8ae86Sav 	int	plen;
3520cc8ae86Sav 	char	model[20];
3530cc8ae86Sav 	pnode_t	node;
35425cf1a30Sjl 
35525cf1a30Sjl 
35625cf1a30Sjl 	if ((error = ddi_soft_state_init(&mc_statep,
35725cf1a30Sjl 	    sizeof (mc_opl_t), 1)) != 0)
35825cf1a30Sjl 		return (error);
35925cf1a30Sjl 
3600cc8ae86Sav 	if ((error = mc_poll_init()) != 0) {
3610cc8ae86Sav 		ddi_soft_state_fini(&mc_statep);
3620cc8ae86Sav 		return (error);
3630cc8ae86Sav 	}
3640cc8ae86Sav 
36525cf1a30Sjl 	mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
36625cf1a30Sjl 	if (&opl_get_mem_unum)
36725cf1a30Sjl 		opl_get_mem_unum = mc_get_mem_unum;
3680cc8ae86Sav 	if (&opl_get_mem_sid)
3690cc8ae86Sav 		opl_get_mem_sid = mc_get_mem_sid;
3700cc8ae86Sav 	if (&opl_get_mem_offset)
3710cc8ae86Sav 		opl_get_mem_offset = mc_get_mem_offset;
3720cc8ae86Sav 	if (&opl_get_mem_addr)
3730cc8ae86Sav 		opl_get_mem_addr = mc_get_mem_addr;
3740cc8ae86Sav 
3750cc8ae86Sav 	node = prom_rootnode();
3760cc8ae86Sav 	plen = prom_getproplen(node, "model");
3770cc8ae86Sav 
3780cc8ae86Sav 	if (plen > 0 && plen < sizeof (model)) {
3790cc8ae86Sav 		(void) prom_getprop(node, "model", model);
3800cc8ae86Sav 		model[plen] = '\0';
3810cc8ae86Sav 		if (strcmp(model, "FF1") == 0)
3820cc8ae86Sav 			plat_model = MODEL_FF1;
3830cc8ae86Sav 		else if (strcmp(model, "FF2") == 0)
3840cc8ae86Sav 			plat_model = MODEL_FF2;
3850cc8ae86Sav 		else if (strncmp(model, "DC", 2) == 0)
3860cc8ae86Sav 			plat_model = MODEL_DC;
3870cc8ae86Sav 	}
38825cf1a30Sjl 
38925cf1a30Sjl 	error =  mod_install(&modlinkage);
39025cf1a30Sjl 	if (error != 0) {
39125cf1a30Sjl 		if (&opl_get_mem_unum)
39225cf1a30Sjl 			opl_get_mem_unum = NULL;
3930cc8ae86Sav 		if (&opl_get_mem_sid)
3940cc8ae86Sav 			opl_get_mem_sid = NULL;
3950cc8ae86Sav 		if (&opl_get_mem_offset)
3960cc8ae86Sav 			opl_get_mem_offset = NULL;
3970cc8ae86Sav 		if (&opl_get_mem_addr)
3980cc8ae86Sav 			opl_get_mem_addr = NULL;
39925cf1a30Sjl 		mutex_destroy(&mcmutex);
4000cc8ae86Sav 		mc_poll_fini();
40125cf1a30Sjl 		ddi_soft_state_fini(&mc_statep);
40225cf1a30Sjl 	}
40325cf1a30Sjl 	return (error);
40425cf1a30Sjl }
40525cf1a30Sjl 
40625cf1a30Sjl int
40725cf1a30Sjl _fini(void)
40825cf1a30Sjl {
40925cf1a30Sjl 	int error;
41025cf1a30Sjl 
41125cf1a30Sjl 	if ((error = mod_remove(&modlinkage)) != 0)
41225cf1a30Sjl 		return (error);
41325cf1a30Sjl 
41425cf1a30Sjl 	if (&opl_get_mem_unum)
41525cf1a30Sjl 		opl_get_mem_unum = NULL;
4160cc8ae86Sav 	if (&opl_get_mem_sid)
4170cc8ae86Sav 		opl_get_mem_sid = NULL;
4180cc8ae86Sav 	if (&opl_get_mem_offset)
4190cc8ae86Sav 		opl_get_mem_offset = NULL;
4200cc8ae86Sav 	if (&opl_get_mem_addr)
4210cc8ae86Sav 		opl_get_mem_addr = NULL;
42225cf1a30Sjl 
4230cc8ae86Sav 	mutex_destroy(&mcmutex);
4240cc8ae86Sav 	mc_poll_fini();
42525cf1a30Sjl 	ddi_soft_state_fini(&mc_statep);
42625cf1a30Sjl 
42725cf1a30Sjl 	return (0);
42825cf1a30Sjl }
42925cf1a30Sjl 
43025cf1a30Sjl int
43125cf1a30Sjl _info(struct modinfo *modinfop)
43225cf1a30Sjl {
43325cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
43425cf1a30Sjl }
43525cf1a30Sjl 
4360cc8ae86Sav static void
4370cc8ae86Sav mc_polling_thread()
4380cc8ae86Sav {
4390cc8ae86Sav 	mutex_enter(&mc_polling_lock);
4400cc8ae86Sav 	mc_pollthr_running = 1;
4410cc8ae86Sav 	while (!(mc_poll_cmd & MC_POLL_EXIT)) {
4420cc8ae86Sav 		mc_polling();
4430cc8ae86Sav 		cv_timedwait(&mc_polling_cv, &mc_polling_lock,
4440cc8ae86Sav 		    ddi_get_lbolt() + mc_timeout_period);
4450cc8ae86Sav 	}
4460cc8ae86Sav 	mc_pollthr_running = 0;
4470cc8ae86Sav 
4480cc8ae86Sav 	/*
4490cc8ae86Sav 	 * signal if any one is waiting for this thread to exit.
4500cc8ae86Sav 	 */
4510cc8ae86Sav 	cv_signal(&mc_poll_exit_cv);
4520cc8ae86Sav 	mutex_exit(&mc_polling_lock);
4530cc8ae86Sav 	thread_exit();
4540cc8ae86Sav 	/* NOTREACHED */
4550cc8ae86Sav }
4560cc8ae86Sav 
4570cc8ae86Sav static int
4580cc8ae86Sav mc_poll_init()
4590cc8ae86Sav {
4600cc8ae86Sav 	mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL);
4610cc8ae86Sav 	cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL);
4620cc8ae86Sav 	cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL);
4630cc8ae86Sav 	return (0);
4640cc8ae86Sav }
4650cc8ae86Sav 
4660cc8ae86Sav static void
4670cc8ae86Sav mc_poll_fini()
4680cc8ae86Sav {
4690cc8ae86Sav 	mutex_enter(&mc_polling_lock);
4700cc8ae86Sav 	if (mc_pollthr_running) {
4710cc8ae86Sav 		mc_poll_cmd = MC_POLL_EXIT;
4720cc8ae86Sav 		cv_signal(&mc_polling_cv);
4730cc8ae86Sav 		while (mc_pollthr_running) {
4740cc8ae86Sav 			cv_wait(&mc_poll_exit_cv, &mc_polling_lock);
4750cc8ae86Sav 		}
4760cc8ae86Sav 	}
4770cc8ae86Sav 	mutex_exit(&mc_polling_lock);
4780cc8ae86Sav 	mutex_destroy(&mc_polling_lock);
4790cc8ae86Sav 	cv_destroy(&mc_polling_cv);
4800cc8ae86Sav 	cv_destroy(&mc_poll_exit_cv);
4810cc8ae86Sav }
4820cc8ae86Sav 
48325cf1a30Sjl static int
48425cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
48525cf1a30Sjl {
48625cf1a30Sjl 	mc_opl_t *mcp;
48725cf1a30Sjl 	int instance;
4880cc8ae86Sav 	int rv;
48925cf1a30Sjl 
49025cf1a30Sjl 	/* get the instance of this devi */
49125cf1a30Sjl 	instance = ddi_get_instance(devi);
49225cf1a30Sjl 
49325cf1a30Sjl 	switch (cmd) {
49425cf1a30Sjl 	case DDI_ATTACH:
49525cf1a30Sjl 		break;
49625cf1a30Sjl 	case DDI_RESUME:
49725cf1a30Sjl 		mcp = ddi_get_soft_state(mc_statep, instance);
4980cc8ae86Sav 		rv = mc_resume(mcp, MC_DRIVER_SUSPENDED);
4990cc8ae86Sav 		return (rv);
50025cf1a30Sjl 	default:
50125cf1a30Sjl 		return (DDI_FAILURE);
50225cf1a30Sjl 	}
50325cf1a30Sjl 
50425cf1a30Sjl 	if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS)
50525cf1a30Sjl 		return (DDI_FAILURE);
50625cf1a30Sjl 
50725cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
50825cf1a30Sjl 		goto bad;
50925cf1a30Sjl 	}
51025cf1a30Sjl 
5110cc8ae86Sav 	if (mc_timeout_period == 0) {
5120cc8ae86Sav 		mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi,
513d8a0cca9Swh 		    DDI_PROP_DONTPASS, "mc-timeout-interval-sec",
514d8a0cca9Swh 		    mc_patrol_interval_sec);
515d8a0cca9Swh 		mc_timeout_period = drv_usectohz(1000000 *
516d8a0cca9Swh 		    mc_patrol_interval_sec / OPL_MAX_BOARDS);
5170cc8ae86Sav 	}
5180cc8ae86Sav 
51925cf1a30Sjl 	/* set informations in mc state */
52025cf1a30Sjl 	mcp->mc_dip = devi;
52125cf1a30Sjl 
52225cf1a30Sjl 	if (mc_board_add(mcp))
52325cf1a30Sjl 		goto bad;
52425cf1a30Sjl 
52525cf1a30Sjl 	insert_mcp(mcp);
5260cc8ae86Sav 
5270cc8ae86Sav 	/*
5280cc8ae86Sav 	 * Start the polling thread if it is not running already.
5290cc8ae86Sav 	 */
5300cc8ae86Sav 	mutex_enter(&mc_polling_lock);
5310cc8ae86Sav 	if (!mc_pollthr_running) {
5320cc8ae86Sav 		(void) thread_create(NULL, 0, (void (*)())mc_polling_thread,
533d8a0cca9Swh 		    NULL, 0, &p0, TS_RUN, mc_poll_priority);
5340cc8ae86Sav 	}
5350cc8ae86Sav 	mutex_exit(&mc_polling_lock);
53625cf1a30Sjl 	ddi_report_dev(devi);
53725cf1a30Sjl 
53825cf1a30Sjl 	return (DDI_SUCCESS);
53925cf1a30Sjl 
54025cf1a30Sjl bad:
54125cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
54225cf1a30Sjl 	return (DDI_FAILURE);
54325cf1a30Sjl }
54425cf1a30Sjl 
54525cf1a30Sjl /* ARGSUSED */
54625cf1a30Sjl static int
54725cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
54825cf1a30Sjl {
5490cc8ae86Sav 	int rv;
55025cf1a30Sjl 	int instance;
55125cf1a30Sjl 	mc_opl_t *mcp;
55225cf1a30Sjl 
55325cf1a30Sjl 	/* get the instance of this devi */
55425cf1a30Sjl 	instance = ddi_get_instance(devi);
55525cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
55625cf1a30Sjl 		return (DDI_FAILURE);
55725cf1a30Sjl 	}
55825cf1a30Sjl 
55925cf1a30Sjl 	switch (cmd) {
56025cf1a30Sjl 	case DDI_SUSPEND:
5610cc8ae86Sav 		rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED);
5620cc8ae86Sav 		return (rv);
56325cf1a30Sjl 	case DDI_DETACH:
56425cf1a30Sjl 		break;
56525cf1a30Sjl 	default:
56625cf1a30Sjl 		return (DDI_FAILURE);
56725cf1a30Sjl 	}
56825cf1a30Sjl 
5690cc8ae86Sav 	delete_mcp(mcp);
57025cf1a30Sjl 	if (mc_board_del(mcp) != DDI_SUCCESS) {
57125cf1a30Sjl 		return (DDI_FAILURE);
57225cf1a30Sjl 	}
57325cf1a30Sjl 
57425cf1a30Sjl 	/* free up the soft state */
57525cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
57625cf1a30Sjl 
57725cf1a30Sjl 	return (DDI_SUCCESS);
57825cf1a30Sjl }
57925cf1a30Sjl 
58025cf1a30Sjl /* ARGSUSED */
58125cf1a30Sjl static int
58225cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
58325cf1a30Sjl {
58425cf1a30Sjl 	return (0);
58525cf1a30Sjl }
58625cf1a30Sjl 
58725cf1a30Sjl /* ARGSUSED */
58825cf1a30Sjl static int
58925cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
59025cf1a30Sjl {
59125cf1a30Sjl 	return (0);
59225cf1a30Sjl }
59325cf1a30Sjl 
59425cf1a30Sjl /* ARGSUSED */
59525cf1a30Sjl static int
59625cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
59725cf1a30Sjl 	int *rvalp)
59825cf1a30Sjl {
5990cc8ae86Sav #ifdef DEBUG
6000cc8ae86Sav 	return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp));
6010cc8ae86Sav #else
60225cf1a30Sjl 	return (ENXIO);
6030cc8ae86Sav #endif
60425cf1a30Sjl }
60525cf1a30Sjl 
60625cf1a30Sjl /*
60725cf1a30Sjl  * PA validity check:
608738dd194Shyw  * This function return 1 if the PA is a valid PA
609738dd194Shyw  * in the running Solaris instance i.e. in physinstall
610738dd194Shyw  * Otherwise, return 0.
61125cf1a30Sjl  */
61225cf1a30Sjl 
61325cf1a30Sjl /* ARGSUSED */
61425cf1a30Sjl static int
61525cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr)
61625cf1a30Sjl {
61725cf1a30Sjl 	if (mcp->mlist == NULL)
61825cf1a30Sjl 		mc_get_mlist(mcp);
61925cf1a30Sjl 
62025cf1a30Sjl 	if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) {
62125cf1a30Sjl 		return (1);
62225cf1a30Sjl 	}
62325cf1a30Sjl 	return (0);
62425cf1a30Sjl }
62525cf1a30Sjl 
62625cf1a30Sjl /*
62725cf1a30Sjl  * mac-pa translation routines.
62825cf1a30Sjl  *
62925cf1a30Sjl  *    Input: mc driver state, (LSB#, Bank#, DIMM address)
63025cf1a30Sjl  *    Output: physical address
63125cf1a30Sjl  *
63225cf1a30Sjl  *    Valid   - return value:  0
63325cf1a30Sjl  *    Invalid - return value: -1
63425cf1a30Sjl  */
63525cf1a30Sjl static int
63625cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa)
63725cf1a30Sjl {
63825cf1a30Sjl 	int i;
63925cf1a30Sjl 	uint64_t pa_offset = 0;
64025cf1a30Sjl 	int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1;
64125cf1a30Sjl 	int bank = maddr->ma_bank;
64225cf1a30Sjl 	mc_addr_t maddr1;
64325cf1a30Sjl 	int bank0, bank1;
64425cf1a30Sjl 
64525cf1a30Sjl 	MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
646d8a0cca9Swh 	    maddr->ma_dimm_addr);
64725cf1a30Sjl 
64825cf1a30Sjl 	/* loc validity check */
64925cf1a30Sjl 	ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd);
65025cf1a30Sjl 	ASSERT(bank >= 0 && OPL_BANK_MAX > bank);
65125cf1a30Sjl 
65225cf1a30Sjl 	/* Do translation */
65325cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
65425cf1a30Sjl 		int pa_bit = 0;
65525cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
65625cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
65725cf1a30Sjl 			pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1;
65825cf1a30Sjl 		} else if (mc_bit == MP_NONE) {
65925cf1a30Sjl 			pa_bit = 0;
66025cf1a30Sjl 		} else if (mc_bit == MP_BANK_0) {
66125cf1a30Sjl 			pa_bit = bank & 1;
66225cf1a30Sjl 		} else if (mc_bit == MP_BANK_1) {
66325cf1a30Sjl 			pa_bit = (bank >> 1) & 1;
66425cf1a30Sjl 		} else if (mc_bit == MP_BANK_2) {
66525cf1a30Sjl 			pa_bit = (bank >> 2) & 1;
66625cf1a30Sjl 		}
66725cf1a30Sjl 		pa_offset |= ((uint64_t)pa_bit) << i;
66825cf1a30Sjl 	}
66925cf1a30Sjl 	*pa = mcp->mc_start_address + pa_offset;
67025cf1a30Sjl 	MC_LOG("pa = %lx\n", *pa);
67125cf1a30Sjl 
67225cf1a30Sjl 	if (pa_to_maddr(mcp, *pa, &maddr1) == -1) {
6730cc8ae86Sav 		cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to "
6740cc8ae86Sav 		    "convert PA %lx\n", maddr->ma_bd, bank,
6750cc8ae86Sav 		    maddr->ma_dimm_addr, *pa);
67625cf1a30Sjl 		return (-1);
67725cf1a30Sjl 	}
67825cf1a30Sjl 
6790cc8ae86Sav 	/*
6800cc8ae86Sav 	 * In mirror mode, PA is always translated to the even bank.
6810cc8ae86Sav 	 */
68225cf1a30Sjl 	if (IS_MIRROR(mcp, maddr->ma_bank)) {
68325cf1a30Sjl 		bank0 = maddr->ma_bank & ~(1);
68425cf1a30Sjl 		bank1 = maddr1.ma_bank & ~(1);
68525cf1a30Sjl 	} else {
68625cf1a30Sjl 		bank0 = maddr->ma_bank;
68725cf1a30Sjl 		bank1 = maddr1.ma_bank;
68825cf1a30Sjl 	}
68925cf1a30Sjl 	/*
69025cf1a30Sjl 	 * there is no need to check ma_bd because it is generated from
69125cf1a30Sjl 	 * mcp.  They are the same.
69225cf1a30Sjl 	 */
693d8a0cca9Swh 	if ((bank0 == bank1) && (maddr->ma_dimm_addr ==
694d8a0cca9Swh 	    maddr1.ma_dimm_addr)) {
69525cf1a30Sjl 		return (0);
69625cf1a30Sjl 	} else {
69725cf1a30Sjl 		cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, "
698d8a0cca9Swh 		    "PA %lx, target /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
699d8a0cca9Swh 		    maddr->ma_dimm_addr, *pa, maddr1.ma_bd, maddr1.ma_bank,
700d8a0cca9Swh 		    maddr1.ma_dimm_addr);
70125cf1a30Sjl 		return (-1);
70225cf1a30Sjl 	}
70325cf1a30Sjl }
70425cf1a30Sjl 
70525cf1a30Sjl /*
70625cf1a30Sjl  * PA to CS (used by pa_to_maddr).
70725cf1a30Sjl  */
70825cf1a30Sjl static int
70925cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset)
71025cf1a30Sjl {
71125cf1a30Sjl 	int i;
712738dd194Shyw 	int cs = 1;
71325cf1a30Sjl 
71425cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
71525cf1a30Sjl 		/* MAC address bit<29> is arranged on the same PA bit */
71625cf1a30Sjl 		/* on both table. So we may use any table. */
71725cf1a30Sjl 		if (mcp->mc_trans_table[0][i] == CS_SHIFT) {
71825cf1a30Sjl 			cs = (pa_offset >> i) & 1;
71925cf1a30Sjl 			break;
72025cf1a30Sjl 		}
72125cf1a30Sjl 	}
72225cf1a30Sjl 	return (cs);
72325cf1a30Sjl }
72425cf1a30Sjl 
72525cf1a30Sjl /*
72625cf1a30Sjl  * PA to DIMM (used by pa_to_maddr).
72725cf1a30Sjl  */
72825cf1a30Sjl /* ARGSUSED */
72925cf1a30Sjl static uint32_t
73025cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset)
73125cf1a30Sjl {
73225cf1a30Sjl 	int i;
73325cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
73425cf1a30Sjl 	uint32_t dimm_addr = 0;
73525cf1a30Sjl 
73625cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
73725cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
73825cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
73925cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
74025cf1a30Sjl 			dimm_addr |= pa_bit_value << mc_bit;
74125cf1a30Sjl 		}
74225cf1a30Sjl 	}
743738dd194Shyw 	dimm_addr |= cs << CS_SHIFT;
74425cf1a30Sjl 	return (dimm_addr);
74525cf1a30Sjl }
74625cf1a30Sjl 
74725cf1a30Sjl /*
74825cf1a30Sjl  * PA to Bank (used by pa_to_maddr).
74925cf1a30Sjl  */
75025cf1a30Sjl static int
75125cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset)
75225cf1a30Sjl {
75325cf1a30Sjl 	int i;
75425cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
75525cf1a30Sjl 	int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT];
75625cf1a30Sjl 
75725cf1a30Sjl 
75825cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
75925cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
76025cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
76125cf1a30Sjl 		switch (mc_bit) {
76225cf1a30Sjl 		case MP_BANK_0:
76325cf1a30Sjl 			bankno |= pa_bit_value;
76425cf1a30Sjl 			break;
76525cf1a30Sjl 		case MP_BANK_1:
76625cf1a30Sjl 			bankno |= pa_bit_value << 1;
76725cf1a30Sjl 			break;
76825cf1a30Sjl 		case MP_BANK_2:
76925cf1a30Sjl 			bankno |= pa_bit_value << 2;
77025cf1a30Sjl 			break;
77125cf1a30Sjl 		}
77225cf1a30Sjl 	}
77325cf1a30Sjl 
77425cf1a30Sjl 	return (bankno);
77525cf1a30Sjl }
77625cf1a30Sjl 
77725cf1a30Sjl /*
77825cf1a30Sjl  * PA to MAC address translation
77925cf1a30Sjl  *
78025cf1a30Sjl  *   Input: MAC driver state, physicall adress
78125cf1a30Sjl  *   Output: LSB#, Bank id, mac address
78225cf1a30Sjl  *
78325cf1a30Sjl  *    Valid   - return value:  0
78425cf1a30Sjl  *    Invalid - return value: -1
78525cf1a30Sjl  */
78625cf1a30Sjl 
78725cf1a30Sjl int
78825cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr)
78925cf1a30Sjl {
79025cf1a30Sjl 	uint64_t pa_offset;
79125cf1a30Sjl 
792738dd194Shyw 	if (!mc_rangecheck_pa(mcp, pa))
79325cf1a30Sjl 		return (-1);
79425cf1a30Sjl 
79525cf1a30Sjl 	/* Do translation */
79625cf1a30Sjl 	pa_offset = pa - mcp->mc_start_address;
79725cf1a30Sjl 
79825cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
799aeb241b2Sav 	maddr->ma_phys_bd = mcp->mc_phys_board_num;
80025cf1a30Sjl 	maddr->ma_bank = pa_to_bank(mcp, pa_offset);
80125cf1a30Sjl 	maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset);
802d8a0cca9Swh 	MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", pa_offset, maddr->ma_bd,
803d8a0cca9Swh 	    maddr->ma_bank, maddr->ma_dimm_addr);
80425cf1a30Sjl 	return (0);
80525cf1a30Sjl }
80625cf1a30Sjl 
8070cc8ae86Sav /*
8080cc8ae86Sav  * UNUM format for DC is "/CMUnn/MEMxyZ", where
8090cc8ae86Sav  *	nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3.
8100cc8ae86Sav  *	x = MAC 0..3
8110cc8ae86Sav  *	y = 0..3 (slot info).
8120cc8ae86Sav  *	Z = 'A' or 'B'
8130cc8ae86Sav  *
8140cc8ae86Sav  * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where
8150cc8ae86Sav  *	x = 0..3 (MEMB number)
8160cc8ae86Sav  *	y = 0..3 (slot info).
8170cc8ae86Sav  *	Z = 'A' or 'B'
8180cc8ae86Sav  *
8190cc8ae86Sav  * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ"
8200cc8ae86Sav  *	x = 0..7 (MEMB number)
8210cc8ae86Sav  *	y = 0..3 (slot info).
8220cc8ae86Sav  *	Z = 'A' or 'B'
8230cc8ae86Sav  */
8240cc8ae86Sav int
825aeb241b2Sav mc_set_mem_unum(char *buf, int buflen, int sb, int bank,
8260cc8ae86Sav     uint32_t mf_type, uint32_t d_slot)
8270cc8ae86Sav {
8280cc8ae86Sav 	char *dimmnm;
8290cc8ae86Sav 	char memb_num;
830aeb241b2Sav 	int cs;
8310cc8ae86Sav 	int i;
832aeb241b2Sav 	int j;
8330cc8ae86Sav 
834aeb241b2Sav 	cs = SLOT_TO_CS(d_slot);
8350cc8ae86Sav 
8360cc8ae86Sav 	if (plat_model == MODEL_DC) {
837056c948bStsien 		if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
838056c948bStsien 		    mf_type == FLT_TYPE_PERMANENT_CE) {
8390cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
8400cc8ae86Sav 			dimmnm = mc_dc_dimm_unum_table[i];
8410cc8ae86Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s",
8420cc8ae86Sav 			    model_names[plat_model].unit_name, sb, dimmnm);
8430cc8ae86Sav 		} else {
8440cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
845aeb241b2Sav 			j = (cs == 0) ?  i : i + 2;
846aeb241b2Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
8470cc8ae86Sav 			    model_names[plat_model].unit_name, sb,
848aeb241b2Sav 			    mc_dc_dimm_unum_table[j],
849aeb241b2Sav 			    mc_dc_dimm_unum_table[j + 1]);
8500cc8ae86Sav 		}
8510cc8ae86Sav 	} else {
852056c948bStsien 		if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
853056c948bStsien 		    mf_type == FLT_TYPE_PERMANENT_CE) {
854aeb241b2Sav 			i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
8550cc8ae86Sav 			dimmnm = mc_ff_dimm_unum_table[i];
8560cc8ae86Sav 			memb_num = dimmnm[0];
8570cc8ae86Sav 			snprintf(buf, buflen, "/%s/%s%c/MEM%s",
8580cc8ae86Sav 			    model_names[plat_model].unit_name,
8590cc8ae86Sav 			    model_names[plat_model].mem_name,
8600cc8ae86Sav 			    memb_num, &dimmnm[1]);
8610cc8ae86Sav 		} else {
8620cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
863aeb241b2Sav 			j = (cs == 0) ?  i : i + 2;
8640cc8ae86Sav 			memb_num = mc_ff_dimm_unum_table[i][0],
865d8a0cca9Swh 			    snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s",
8660cc8ae86Sav 			    model_names[plat_model].unit_name,
8670cc8ae86Sav 			    model_names[plat_model].mem_name, memb_num,
868aeb241b2Sav 			    &mc_ff_dimm_unum_table[j][1],
869aeb241b2Sav 			    &mc_ff_dimm_unum_table[j + 1][1]);
8700cc8ae86Sav 		}
8710cc8ae86Sav 	}
8720cc8ae86Sav 	return (0);
8730cc8ae86Sav }
8740cc8ae86Sav 
87525cf1a30Sjl static void
87625cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt)
87725cf1a30Sjl {
87825cf1a30Sjl 	char buf[FM_MAX_CLASS];
87925cf1a30Sjl 	char device_path[MAXPATHLEN];
8800cc8ae86Sav 	char sid[MAXPATHLEN];
88125cf1a30Sjl 	nv_alloc_t *nva = NULL;
88225cf1a30Sjl 	nvlist_t *ereport, *detector, *resource;
88325cf1a30Sjl 	errorq_elem_t *eqep;
88425cf1a30Sjl 	int nflts;
88525cf1a30Sjl 	mc_flt_stat_t *flt_stat;
8860cc8ae86Sav 	int i, n;
8870cc8ae86Sav 	int blen = MAXPATHLEN;
8880cc8ae86Sav 	char *p, *s = NULL;
88925cf1a30Sjl 	uint32_t values[2], synd[2], dslot[2];
8900cc8ae86Sav 	uint64_t offset = (uint64_t)-1;
8910cc8ae86Sav 	int ret = -1;
89225cf1a30Sjl 
89325cf1a30Sjl 	if (panicstr) {
89425cf1a30Sjl 		eqep = errorq_reserve(ereport_errorq);
89525cf1a30Sjl 		if (eqep == NULL)
89625cf1a30Sjl 			return;
89725cf1a30Sjl 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
89825cf1a30Sjl 		nva = errorq_elem_nva(ereport_errorq, eqep);
89925cf1a30Sjl 	} else {
90025cf1a30Sjl 		ereport = fm_nvlist_create(nva);
90125cf1a30Sjl 	}
90225cf1a30Sjl 
90325cf1a30Sjl 	/*
90425cf1a30Sjl 	 * Create the scheme "dev" FMRI.
90525cf1a30Sjl 	 */
90625cf1a30Sjl 	detector = fm_nvlist_create(nva);
90725cf1a30Sjl 	resource = fm_nvlist_create(nva);
90825cf1a30Sjl 
90925cf1a30Sjl 	nflts = mc_aflt->mflt_nflts;
91025cf1a30Sjl 
91125cf1a30Sjl 	ASSERT(nflts >= 1 && nflts <= 2);
91225cf1a30Sjl 
91325cf1a30Sjl 	flt_stat = mc_aflt->mflt_stat[0];
91425cf1a30Sjl 	(void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path);
91525cf1a30Sjl 	(void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
91625cf1a30Sjl 	    device_path, NULL);
91725cf1a30Sjl 
91825cf1a30Sjl 	/*
91925cf1a30Sjl 	 * Encode all the common data into the ereport.
92025cf1a30Sjl 	 */
921d8a0cca9Swh 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", MC_OPL_ERROR_CLASS,
922d8a0cca9Swh 	    mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : MC_OPL_MI_SUBCLASS,
923d8a0cca9Swh 	    mc_aflt->mflt_erpt_class);
92425cf1a30Sjl 
92525cf1a30Sjl 	MC_LOG("mc_ereport_post: ereport %s\n", buf);
92625cf1a30Sjl 
92725cf1a30Sjl 
92825cf1a30Sjl 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
929d8a0cca9Swh 	    fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), detector, NULL);
93025cf1a30Sjl 
93125cf1a30Sjl 	/*
93225cf1a30Sjl 	 * Set payload.
93325cf1a30Sjl 	 */
93425cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32,
935d8a0cca9Swh 	    flt_stat->mf_flt_maddr.ma_bd, NULL);
93625cf1a30Sjl 
93725cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64,
938d8a0cca9Swh 	    flt_stat->mf_flt_paddr, NULL);
93925cf1a30Sjl 
940056c948bStsien 	if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE ||
941056c948bStsien 	    flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
942d8a0cca9Swh 		fm_payload_set(ereport, MC_OPL_FLT_TYPE, DATA_TYPE_UINT8,
943d8a0cca9Swh 		    ECC_STICKY, NULL);
94425cf1a30Sjl 	}
94525cf1a30Sjl 
94625cf1a30Sjl 	for (i = 0; i < nflts; i++)
94725cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank;
94825cf1a30Sjl 
949d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, nflts,
950d8a0cca9Swh 	    values, NULL);
95125cf1a30Sjl 
95225cf1a30Sjl 	for (i = 0; i < nflts; i++)
95325cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_cntl;
95425cf1a30Sjl 
955d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, nflts,
956d8a0cca9Swh 	    values, NULL);
95725cf1a30Sjl 
95825cf1a30Sjl 	for (i = 0; i < nflts; i++)
95925cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_add;
96025cf1a30Sjl 
961056c948bStsien 	/* offset is set only for PCE and ICE */
962056c948bStsien 	if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_INTERMITTENT_CE ||
963056c948bStsien 	    mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) {
9640cc8ae86Sav 		offset = values[0];
9650cc8ae86Sav 
9660cc8ae86Sav 	}
967d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, nflts,
968d8a0cca9Swh 	    values, NULL);
96925cf1a30Sjl 
97025cf1a30Sjl 	for (i = 0; i < nflts; i++)
97125cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_log;
97225cf1a30Sjl 
973d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, nflts,
974d8a0cca9Swh 	    values, NULL);
97525cf1a30Sjl 
97625cf1a30Sjl 	for (i = 0; i < nflts; i++) {
97725cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
97825cf1a30Sjl 		if (flt_stat->mf_errlog_valid) {
97925cf1a30Sjl 			synd[i] = flt_stat->mf_synd;
98025cf1a30Sjl 			dslot[i] = flt_stat->mf_dimm_slot;
98125cf1a30Sjl 			values[i] = flt_stat->mf_dram_place;
98225cf1a30Sjl 		} else {
98325cf1a30Sjl 			synd[i] = 0;
98425cf1a30Sjl 			dslot[i] = 0;
98525cf1a30Sjl 			values[i] = 0;
98625cf1a30Sjl 		}
98725cf1a30Sjl 	}
98825cf1a30Sjl 
989d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_SYND, DATA_TYPE_UINT32_ARRAY, nflts,
990d8a0cca9Swh 	    synd, NULL);
99125cf1a30Sjl 
992d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, DATA_TYPE_UINT32_ARRAY,
993d8a0cca9Swh 	    nflts, dslot, NULL);
99425cf1a30Sjl 
995d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_DRAM, DATA_TYPE_UINT32_ARRAY, nflts,
996d8a0cca9Swh 	    values, NULL);
99725cf1a30Sjl 
99825cf1a30Sjl 	device_path[0] = 0;
99925cf1a30Sjl 	p = &device_path[0];
10000cc8ae86Sav 	sid[0] = 0;
10010cc8ae86Sav 	s = &sid[0];
10020cc8ae86Sav 	ret = 0;
100325cf1a30Sjl 
100425cf1a30Sjl 	for (i = 0; i < nflts; i++) {
10050cc8ae86Sav 		int bank;
100625cf1a30Sjl 
100725cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
10080cc8ae86Sav 		bank = flt_stat->mf_flt_maddr.ma_bank;
1009d8a0cca9Swh 		ret = mc_set_mem_unum(p + strlen(p), blen,
1010d8a0cca9Swh 		    flt_stat->mf_flt_maddr.ma_phys_bd, bank, flt_stat->mf_type,
1011d8a0cca9Swh 		    flt_stat->mf_dimm_slot);
10120cc8ae86Sav 
10130cc8ae86Sav 		if (ret != 0) {
10140cc8ae86Sav 			cmn_err(CE_WARN,
10150cc8ae86Sav 			    "mc_ereport_post: Failed to determine the unum "
10160cc8ae86Sav 			    "for board=%d bank=%d type=0x%x slot=0x%x",
10170cc8ae86Sav 			    flt_stat->mf_flt_maddr.ma_bd, bank,
10180cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10190cc8ae86Sav 			continue;
102025cf1a30Sjl 		}
10210cc8ae86Sav 		n = strlen(device_path);
102225cf1a30Sjl 		blen = MAXPATHLEN - n;
102325cf1a30Sjl 		p = &device_path[n];
102425cf1a30Sjl 		if (i < (nflts - 1)) {
102525cf1a30Sjl 			snprintf(p, blen, " ");
10260cc8ae86Sav 			blen--;
10270cc8ae86Sav 			p++;
10280cc8ae86Sav 		}
10290cc8ae86Sav 
10300cc8ae86Sav 		if (ret == 0) {
10310cc8ae86Sav 			ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s),
1032aeb241b2Sav 			    blen, flt_stat->mf_flt_maddr.ma_phys_bd, bank,
10330cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10340cc8ae86Sav 
103525cf1a30Sjl 		}
103625cf1a30Sjl 	}
103725cf1a30Sjl 
1038d8a0cca9Swh 	(void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, NULL,
1039d8a0cca9Swh 	    device_path, (ret == 0) ? sid : NULL, (ret == 0) ? offset :
1040d8a0cca9Swh 	    (uint64_t)-1);
104125cf1a30Sjl 
1042d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, resource,
1043d8a0cca9Swh 	    NULL);
104425cf1a30Sjl 
104525cf1a30Sjl 	if (panicstr) {
104625cf1a30Sjl 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
104725cf1a30Sjl 	} else {
104825cf1a30Sjl 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
104925cf1a30Sjl 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
105025cf1a30Sjl 		fm_nvlist_destroy(detector, FM_NVA_FREE);
105125cf1a30Sjl 		fm_nvlist_destroy(resource, FM_NVA_FREE);
105225cf1a30Sjl 	}
105325cf1a30Sjl }
105425cf1a30Sjl 
10550cc8ae86Sav 
105625cf1a30Sjl static void
105725cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt)
105825cf1a30Sjl {
105925cf1a30Sjl 	int rv;
106025cf1a30Sjl 	uint64_t pa = (uint64_t)(-1);
10610cc8ae86Sav 	int i;
106225cf1a30Sjl 
1063d8a0cca9Swh 	MC_LOG("mc_err_drain: %s\n", mc_aflt->mflt_erpt_class);
106425cf1a30Sjl 	/*
106525cf1a30Sjl 	 * we come here only when we have:
10661039f409Sav 	 * In mirror mode: MUE, SUE
1067056c948bStsien 	 * In normal mode: UE, Permanent CE, Intermittent CE
106825cf1a30Sjl 	 */
10690cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
10700cc8ae86Sav 		rv = mcaddr_to_pa(mc_aflt->mflt_mcp,
1071d8a0cca9Swh 		    &(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa);
1072738dd194Shyw 
1073738dd194Shyw 		/* Ensure the pa is valid (not in isolated memory block) */
1074738dd194Shyw 		if (rv == 0 && pa_is_valid(mc_aflt->mflt_mcp, pa))
10750cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = pa;
10760cc8ae86Sav 		else
10770cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1;
10780cc8ae86Sav 	}
10790cc8ae86Sav 
1080738dd194Shyw 	MC_LOG("mc_err_drain:pa = %lx\n", pa);
108125cf1a30Sjl 
1082738dd194Shyw 	switch (page_retire_check(pa, NULL)) {
1083738dd194Shyw 	case 0:
1084738dd194Shyw 	case EAGAIN:
1085738dd194Shyw 		MC_LOG("Page retired or pending\n");
1086738dd194Shyw 		return;
1087738dd194Shyw 	case EIO:
1088738dd194Shyw 		/*
1089056c948bStsien 		 * Do page retirement except for the PCE and ICE cases.
1090738dd194Shyw 		 * This is taken care by the OPL DE
1091738dd194Shyw 		 */
1092056c948bStsien 		if (mc_aflt->mflt_stat[0]->mf_type !=
1093056c948bStsien 		    FLT_TYPE_INTERMITTENT_CE &&
1094056c948bStsien 		    mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) {
1095738dd194Shyw 			MC_LOG("offline page at pa %lx error %x\n", pa,
1096d8a0cca9Swh 			    mc_aflt->mflt_pr);
1097738dd194Shyw 			(void) page_retire(pa, mc_aflt->mflt_pr);
109825cf1a30Sjl 		}
1099738dd194Shyw 		break;
1100738dd194Shyw 	case EINVAL:
1101738dd194Shyw 	default:
1102738dd194Shyw 		/*
1103738dd194Shyw 		 * Some memory do not have page structure so
1104738dd194Shyw 		 * we keep going in case of EINVAL.
1105738dd194Shyw 		 */
1106738dd194Shyw 		break;
110725cf1a30Sjl 	}
110825cf1a30Sjl 
11090cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
11100cc8ae86Sav 		mc_aflt_t mc_aflt0;
11110cc8ae86Sav 		if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) {
11120cc8ae86Sav 			mc_aflt0 = *mc_aflt;
11130cc8ae86Sav 			mc_aflt0.mflt_nflts = 1;
11140cc8ae86Sav 			mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i];
11150cc8ae86Sav 			mc_ereport_post(&mc_aflt0);
11160cc8ae86Sav 		}
11170cc8ae86Sav 	}
11180cc8ae86Sav }
111925cf1a30Sjl 
112025cf1a30Sjl /*
112125cf1a30Sjl  * The restart address is actually defined in unit of PA[37:6]
112225cf1a30Sjl  * the mac patrol will convert that to dimm offset.  If the
112325cf1a30Sjl  * address is not in the bank, it will continue to search for
112425cf1a30Sjl  * the next PA that is within the bank.
112525cf1a30Sjl  *
112625cf1a30Sjl  * Also the mac patrol scans the dimms based on PA, not
112725cf1a30Sjl  * dimm offset.
112825cf1a30Sjl  */
112925cf1a30Sjl static int
1130738dd194Shyw restart_patrol(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr_info)
113125cf1a30Sjl {
113225cf1a30Sjl 	uint64_t pa;
113325cf1a30Sjl 	int rv;
113425cf1a30Sjl 
1135*601c2e1eSdhain 	if (MC_REWRITE_MODE(mcp, bank)) {
1136*601c2e1eSdhain 		return (0);
1137*601c2e1eSdhain 	}
1138738dd194Shyw 	if (rsaddr_info == NULL || (rsaddr_info->mi_valid == 0)) {
113925cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
114025cf1a30Sjl 		return (0);
114125cf1a30Sjl 	}
114225cf1a30Sjl 
1143738dd194Shyw 	rv = mcaddr_to_pa(mcp, &rsaddr_info->mi_restartaddr, &pa);
114425cf1a30Sjl 	if (rv != 0) {
114525cf1a30Sjl 		MC_LOG("cannot convert mcaddr to pa. use auto restart\n");
114625cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
114725cf1a30Sjl 		return (0);
114825cf1a30Sjl 	}
114925cf1a30Sjl 
1150738dd194Shyw 	if (!mc_rangecheck_pa(mcp, pa)) {
115125cf1a30Sjl 		/* pa is not on this board, just retry */
115225cf1a30Sjl 		cmn_err(CE_WARN, "restart_patrol: invalid address %lx "
1153d8a0cca9Swh 		    "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 
1160738dd194Shyw 	if (!rsaddr_info->mi_injectrestart) {
1161738dd194Shyw 		/*
11621039f409Sav 		 * For non-error injection restart we need to
1163738dd194Shyw 		 * determine if the current restart pa/page is
1164738dd194Shyw 		 * a "good" page. A "good" page is a page that
1165738dd194Shyw 		 * has not been page retired. If the current
1166738dd194Shyw 		 * page that contains the pa is "good", we will
1167738dd194Shyw 		 * do a HW auto restart and let HW patrol continue
1168738dd194Shyw 		 * where it last stopped. Most desired scenario.
1169738dd194Shyw 		 *
1170738dd194Shyw 		 * If the current page is not "good", we will advance
1171738dd194Shyw 		 * to the next page to find the next "good" page and
1172738dd194Shyw 		 * restart the patrol from there.
1173738dd194Shyw 		 */
1174738dd194Shyw 		int wrapcount = 0;
1175738dd194Shyw 		uint64_t origpa = pa;
1176738dd194Shyw 		while (wrapcount < 2) {
1177d8a0cca9Swh 			if (!pa_is_valid(mcp, pa)) {
1178*601c2e1eSdhain 			/*
1179*601c2e1eSdhain 			 * Not in physinstall - advance to the
1180*601c2e1eSdhain 			 * next memory isolation blocksize
1181*601c2e1eSdhain 			 */
1182*601c2e1eSdhain 			MC_LOG("Invalid PA\n");
1183*601c2e1eSdhain 			pa = roundup(pa + 1, mc_isolation_bsize);
1184d8a0cca9Swh 			} else {
1185*601c2e1eSdhain 			int rv;
1186*601c2e1eSdhain 			if ((rv = page_retire_check(pa, NULL)) != 0 &&
1187*601c2e1eSdhain 			    rv != EAGAIN) {
1188d8a0cca9Swh 					/*
1189d8a0cca9Swh 					 * The page is "good" (not retired),
1190d8a0cca9Swh 					 * we will use automatic HW restart
1191d8a0cca9Swh 					 * algorithm if this is the original
1192d8a0cca9Swh 					 * current starting page.
1193d8a0cca9Swh 					 */
1194*601c2e1eSdhain 				if (pa == origpa) {
1195*601c2e1eSdhain 					MC_LOG("Page has no error. "
1196*601c2e1eSdhain 					    "Auto restart\n");
1197*601c2e1eSdhain 					MAC_PTRL_START(mcp, bank);
1198*601c2e1eSdhain 					return (0);
1199*601c2e1eSdhain 				} else {
1200*601c2e1eSdhain 					/*
1201*601c2e1eSdhain 					 * found a subsequent good page
1202*601c2e1eSdhain 					 */
1203*601c2e1eSdhain 					break;
1204738dd194Shyw 				}
1205*601c2e1eSdhain 			}
1206738dd194Shyw 
1207*601c2e1eSdhain 			/*
1208*601c2e1eSdhain 			 * Skip to the next page
1209*601c2e1eSdhain 			 */
1210*601c2e1eSdhain 			pa = roundup(pa + 1, PAGESIZE);
1211*601c2e1eSdhain 			MC_LOG("Skipping bad page to %lx\n", pa);
1212d8a0cca9Swh 			}
1213738dd194Shyw 
1214*601c2e1eSdhain 		    /* Check to see if we hit the end of the memory range */
1215d8a0cca9Swh 			if (pa >= (mcp->mc_start_address + mcp->mc_size)) {
1216*601c2e1eSdhain 			MC_LOG("Wrap around\n");
1217*601c2e1eSdhain 			pa = mcp->mc_start_address;
1218*601c2e1eSdhain 			wrapcount++;
1219d8a0cca9Swh 			}
1220738dd194Shyw 		}
1221738dd194Shyw 
1222738dd194Shyw 		if (wrapcount > 1) {
1223d8a0cca9Swh 			MC_LOG("Failed to find a good page. Just restart\n");
1224d8a0cca9Swh 			MAC_PTRL_START(mcp, bank);
1225d8a0cca9Swh 			return (0);
122625cf1a30Sjl 		}
122725cf1a30Sjl 	}
122825cf1a30Sjl 
1229738dd194Shyw 	/*
1230738dd194Shyw 	 * We reached here either:
1231738dd194Shyw 	 * 1. We are doing an error injection restart that specify
1232738dd194Shyw 	 *    the exact pa/page to restart. OR
1233738dd194Shyw 	 * 2. We found a subsequent good page different from the
1234738dd194Shyw 	 *    original restart pa/page.
1235738dd194Shyw 	 * Restart MAC patrol: PA[37:6]
1236738dd194Shyw 	 */
123725cf1a30Sjl 	MC_LOG("restart at pa = %lx\n", pa);
123825cf1a30Sjl 	ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa));
123925cf1a30Sjl 	MAC_PTRL_START_ADD(mcp, bank);
124025cf1a30Sjl 
124125cf1a30Sjl 	return (0);
124225cf1a30Sjl }
124325cf1a30Sjl 
1244*601c2e1eSdhain static void
1245*601c2e1eSdhain mc_retry_info_put(mc_retry_info_t **q, mc_retry_info_t *p)
1246*601c2e1eSdhain {
1247*601c2e1eSdhain 	ASSERT(p != NULL);
1248*601c2e1eSdhain 	p->ri_next = *q;
1249*601c2e1eSdhain 	*q = p;
1250*601c2e1eSdhain }
1251*601c2e1eSdhain 
1252*601c2e1eSdhain static mc_retry_info_t *
1253*601c2e1eSdhain mc_retry_info_get(mc_retry_info_t **q)
1254*601c2e1eSdhain {
1255*601c2e1eSdhain 	mc_retry_info_t *p;
1256*601c2e1eSdhain 
1257*601c2e1eSdhain 	if ((p = *q) != NULL) {
1258*601c2e1eSdhain 		*q = p->ri_next;
1259*601c2e1eSdhain 		return (p);
1260*601c2e1eSdhain 	} else {
1261*601c2e1eSdhain 		return (NULL);
1262*601c2e1eSdhain 	}
1263*601c2e1eSdhain }
1264*601c2e1eSdhain 
126525cf1a30Sjl /*
126625cf1a30Sjl  * Rewriting is used for two purposes.
126725cf1a30Sjl  *  - to correct the error in memory.
126825cf1a30Sjl  *  - to determine whether the error is permanent or intermittent.
126925cf1a30Sjl  * It's done by writing the address in MAC_BANKm_REWRITE_ADD
127025cf1a30Sjl  * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that,
127125cf1a30Sjl  * REW_END (and REW_CE/REW_UE if some error detected) is set when
127225cf1a30Sjl  * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM.
127325cf1a30Sjl  *
127425cf1a30Sjl  * Note that rewrite operation doesn't change RAW_UE to Marked UE.
127525cf1a30Sjl  * Therefore, we use it only CE case.
127625cf1a30Sjl  */
1277*601c2e1eSdhain 
127825cf1a30Sjl static uint32_t
1279*601c2e1eSdhain do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr, int retrying)
128025cf1a30Sjl {
128125cf1a30Sjl 	uint32_t cntl;
128225cf1a30Sjl 	int count = 0;
1283*601c2e1eSdhain 	int max_count;
1284*601c2e1eSdhain 	int retry_state;
1285*601c2e1eSdhain 
1286*601c2e1eSdhain 	if (retrying)
1287*601c2e1eSdhain 		max_count = 1;
1288*601c2e1eSdhain 	else
1289*601c2e1eSdhain 		max_count = mc_max_rewrite_loop;
1290*601c2e1eSdhain 
1291*601c2e1eSdhain 	retry_state = RETRY_STATE_PENDING;
1292*601c2e1eSdhain 
1293*601c2e1eSdhain 	if (!retrying && MC_REWRITE_MODE(mcp, bank)) {
1294*601c2e1eSdhain 		goto timeout;
1295*601c2e1eSdhain 	}
1296*601c2e1eSdhain 
1297*601c2e1eSdhain 	retry_state = RETRY_STATE_ACTIVE;
129825cf1a30Sjl 
129925cf1a30Sjl 	/* first wait to make sure PTRL_STATUS is 0 */
1300*601c2e1eSdhain 	while (count++ < max_count) {
130125cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
1302*601c2e1eSdhain 		if (!(cntl & MAC_CNTL_PTRL_STATUS)) {
1303*601c2e1eSdhain 			count = 0;
130425cf1a30Sjl 			break;
1305*601c2e1eSdhain 		}
13060cc8ae86Sav 		drv_usecwait(mc_rewrite_delay);
130725cf1a30Sjl 	}
1308*601c2e1eSdhain 	if (count >= max_count)
1309*601c2e1eSdhain 		goto timeout;
131025cf1a30Sjl 
131125cf1a30Sjl 	count = 0;
131225cf1a30Sjl 
131325cf1a30Sjl 	ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr);
131425cf1a30Sjl 	MAC_REW_REQ(mcp, bank);
131525cf1a30Sjl 
1316*601c2e1eSdhain 	retry_state = RETRY_STATE_REWRITE;
1317*601c2e1eSdhain 
131825cf1a30Sjl 	do {
1319*601c2e1eSdhain 		if (count++ > max_count) {
1320*601c2e1eSdhain 			goto timeout;
13210cc8ae86Sav 		} else {
13220cc8ae86Sav 			drv_usecwait(mc_rewrite_delay);
13230cc8ae86Sav 		}
1324*601c2e1eSdhain 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
132525cf1a30Sjl 	/*
132625cf1a30Sjl 	 * If there are other MEMORY or PCI activities, this
132725cf1a30Sjl 	 * will be BUSY, else it should be set immediately
132825cf1a30Sjl 	 */
132925cf1a30Sjl 	} while (!(cntl & MAC_CNTL_REW_END));
133025cf1a30Sjl 
133125cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
133225cf1a30Sjl 	return (cntl);
1333*601c2e1eSdhain timeout:
1334*601c2e1eSdhain 	mc_set_rewrite(mcp, bank, dimm_addr, retry_state);
1335*601c2e1eSdhain 
1336*601c2e1eSdhain 	return (0);
1337*601c2e1eSdhain }
1338*601c2e1eSdhain 
1339*601c2e1eSdhain void
1340*601c2e1eSdhain mc_clear_rewrite(mc_opl_t *mcp, int bank)
1341*601c2e1eSdhain {
1342*601c2e1eSdhain 	struct mc_bank *bankp;
1343*601c2e1eSdhain 	mc_retry_info_t *retry;
1344*601c2e1eSdhain 	uint32_t rew_addr;
1345*601c2e1eSdhain 
1346*601c2e1eSdhain 	bankp = &(mcp->mc_bank[bank]);
1347*601c2e1eSdhain 	retry = bankp->mcb_active;
1348*601c2e1eSdhain 	bankp->mcb_active = NULL;
1349*601c2e1eSdhain 	mc_retry_info_put(&bankp->mcb_retry_freelist, retry);
1350*601c2e1eSdhain 
1351*601c2e1eSdhain again:
1352*601c2e1eSdhain 	bankp->mcb_rewrite_count = 0;
1353*601c2e1eSdhain 
1354*601c2e1eSdhain 	while (retry = mc_retry_info_get(&bankp->mcb_retry_pending)) {
1355*601c2e1eSdhain 		rew_addr = retry->ri_addr;
1356*601c2e1eSdhain 		mc_retry_info_put(&bankp->mcb_retry_freelist, retry);
1357*601c2e1eSdhain 		if (do_rewrite(mcp, bank, rew_addr, 1) == 0)
1358*601c2e1eSdhain 			break;
1359*601c2e1eSdhain 	}
1360*601c2e1eSdhain 
1361*601c2e1eSdhain 	/* we break out if no more pending rewrite or we got timeout again */
1362*601c2e1eSdhain 
1363*601c2e1eSdhain 	if (!bankp->mcb_active && !bankp->mcb_retry_pending) {
1364*601c2e1eSdhain 		if (!IS_MIRROR(mcp, bank)) {
1365*601c2e1eSdhain 			MC_CLEAR_REWRITE_MODE(mcp, bank);
1366*601c2e1eSdhain 		} else {
1367*601c2e1eSdhain 			int mbank = bank ^ 1;
1368*601c2e1eSdhain 			bankp = &(mcp->mc_bank[mbank]);
1369*601c2e1eSdhain 			if (!bankp->mcb_active && !bankp->mcb_retry_pending) {
1370*601c2e1eSdhain 			MC_CLEAR_REWRITE_MODE(mcp, bank);
1371*601c2e1eSdhain 			MC_CLEAR_REWRITE_MODE(mcp, mbank);
1372*601c2e1eSdhain 			} else {
1373*601c2e1eSdhain 			bank = mbank;
1374*601c2e1eSdhain 			goto again;
1375*601c2e1eSdhain 			}
1376*601c2e1eSdhain 		}
1377*601c2e1eSdhain 	}
137825cf1a30Sjl }
1379*601c2e1eSdhain 
1380*601c2e1eSdhain void
1381*601c2e1eSdhain mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state)
1382*601c2e1eSdhain {
1383*601c2e1eSdhain 	mc_retry_info_t *retry;
1384*601c2e1eSdhain 	struct mc_bank *bankp;
1385*601c2e1eSdhain 
1386*601c2e1eSdhain 	bankp = &mcp->mc_bank[bank];
1387*601c2e1eSdhain 
1388*601c2e1eSdhain 	retry = mc_retry_info_get(&bankp->mcb_retry_freelist);
1389*601c2e1eSdhain 
1390*601c2e1eSdhain 	ASSERT(retry != NULL);
1391*601c2e1eSdhain 
1392*601c2e1eSdhain 	retry->ri_addr = addr;
1393*601c2e1eSdhain 	retry->ri_state = state;
1394*601c2e1eSdhain 
1395*601c2e1eSdhain 	MC_SET_REWRITE_MODE(mcp, bank);
1396*601c2e1eSdhain 
1397*601c2e1eSdhain 	if ((state > RETRY_STATE_PENDING)) {
1398*601c2e1eSdhain 		ASSERT(bankp->mcb_active == NULL);
1399*601c2e1eSdhain 		bankp->mcb_active = retry;
1400*601c2e1eSdhain 	} else {
1401*601c2e1eSdhain 		mc_retry_info_put(&bankp->mcb_retry_pending, retry);
1402*601c2e1eSdhain 	}
1403*601c2e1eSdhain 
1404*601c2e1eSdhain 	if (IS_MIRROR(mcp, bank)) {
1405*601c2e1eSdhain 		int mbank = bank ^1;
1406*601c2e1eSdhain 		MC_SET_REWRITE_MODE(mcp, mbank);
1407*601c2e1eSdhain 	}
1408*601c2e1eSdhain }
1409*601c2e1eSdhain 
141025cf1a30Sjl void
141125cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp)
141225cf1a30Sjl {
14130cc8ae86Sav 	int count;
14140cc8ae86Sav 	int n = 0;
141525cf1a30Sjl 	scf_log_t *p;
141625cf1a30Sjl 	int bank;
141725cf1a30Sjl 
14180cc8ae86Sav 	for (bank = 0; bank < BANKNUM_PER_SB; bank++) {
1419d8a0cca9Swh 		while ((p = mcp->mc_scf_log[bank]) != NULL &&
1420d8a0cca9Swh 		    (n < mc_max_errlog_processed)) {
1421*601c2e1eSdhain 		ASSERT(bank == p->sl_bank);
1422*601c2e1eSdhain 		count = 0;
1423*601c2e1eSdhain 		while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank))
1424*601c2e1eSdhain 		    & MAC_STATIC_ERR_VLD)) {
1425*601c2e1eSdhain 			if (count++ >= (mc_max_scf_loop)) {
1426*601c2e1eSdhain 				break;
142725cf1a30Sjl 			}
1428*601c2e1eSdhain 			drv_usecwait(mc_scf_delay);
1429*601c2e1eSdhain 		}
143025cf1a30Sjl 
1431*601c2e1eSdhain 		if (count < mc_max_scf_loop) {
1432*601c2e1eSdhain 			ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank),
1433*601c2e1eSdhain 			    p->sl_err_log);
143425cf1a30Sjl 
1435*601c2e1eSdhain 			ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank),
1436*601c2e1eSdhain 			    p->sl_err_add|MAC_STATIC_ERR_VLD);
1437*601c2e1eSdhain 			mcp->mc_scf_retry[bank] = 0;
1438*601c2e1eSdhain 		} else {
1439*601c2e1eSdhain 			/*
1440*601c2e1eSdhain 			 * if we try too many times, just drop the req
1441*601c2e1eSdhain 			 */
1442*601c2e1eSdhain 			if (mcp->mc_scf_retry[bank]++ <=
1443*601c2e1eSdhain 			    mc_max_scf_retry) {
1444*601c2e1eSdhain 				return;
144525cf1a30Sjl 			} else {
1446*601c2e1eSdhain 				if ((++mc_pce_dropped & 0xff) == 0) {
1447*601c2e1eSdhain 					cmn_err(CE_WARN, "Cannot "
1448*601c2e1eSdhain 					    "report Permanent CE to "
1449*601c2e1eSdhain 					    "SCF\n");
1450d8a0cca9Swh 				}
145125cf1a30Sjl 			}
1452*601c2e1eSdhain 		}
1453*601c2e1eSdhain 		n++;
1454*601c2e1eSdhain 		mcp->mc_scf_log[bank] = p->sl_next;
1455*601c2e1eSdhain 		mcp->mc_scf_total[bank]--;
1456*601c2e1eSdhain 		ASSERT(mcp->mc_scf_total[bank] >= 0);
1457*601c2e1eSdhain 		kmem_free(p, sizeof (scf_log_t));
145825cf1a30Sjl 		}
145925cf1a30Sjl 	}
146025cf1a30Sjl }
146125cf1a30Sjl void
146225cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank)
146325cf1a30Sjl {
146425cf1a30Sjl 	scf_log_t *p;
146525cf1a30Sjl 
14660cc8ae86Sav 	if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) {
14670cc8ae86Sav 		if ((++mc_pce_dropped & 0xff) == 0) {
1468d8a0cca9Swh 			cmn_err(CE_WARN, "Too many Permanent CE requests.\n");
14690cc8ae86Sav 		}
147025cf1a30Sjl 		return;
147125cf1a30Sjl 	}
147225cf1a30Sjl 	p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP);
147325cf1a30Sjl 	p->sl_next = 0;
147425cf1a30Sjl 	p->sl_err_add = flt_stat->mf_err_add;
147525cf1a30Sjl 	p->sl_err_log = flt_stat->mf_err_log;
147625cf1a30Sjl 	p->sl_bank = bank;
147725cf1a30Sjl 
14780cc8ae86Sav 	if (mcp->mc_scf_log[bank] == NULL) {
147925cf1a30Sjl 		/*
148025cf1a30Sjl 		 * we rely on mc_scf_log to detect NULL queue.
148125cf1a30Sjl 		 * mc_scf_log_tail is irrelevant is such case.
148225cf1a30Sjl 		 */
14830cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p;
148425cf1a30Sjl 	} else {
14850cc8ae86Sav 		mcp->mc_scf_log_tail[bank]->sl_next = p;
14860cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = p;
148725cf1a30Sjl 	}
14880cc8ae86Sav 	mcp->mc_scf_total[bank]++;
148925cf1a30Sjl }
149025cf1a30Sjl /*
149125cf1a30Sjl  * This routine determines what kind of CE happens, intermittent
149225cf1a30Sjl  * or permanent as follows. (See 4.7.3 in Columbus2 PRM.)
149325cf1a30Sjl  * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register.
149425cf1a30Sjl  * - If CE is still detected on the same address even after doing
149525cf1a30Sjl  *   rewrite operation twice, it is determined as permanent error.
149625cf1a30Sjl  * - If error is not detected anymore, it is determined as intermittent
149725cf1a30Sjl  *   error.
149825cf1a30Sjl  * - If UE is detected due to rewrite operation, it should be treated
149925cf1a30Sjl  *   as UE.
150025cf1a30Sjl  */
150125cf1a30Sjl 
150225cf1a30Sjl /* ARGSUSED */
150325cf1a30Sjl static void
150425cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error)
150525cf1a30Sjl {
150625cf1a30Sjl 	uint32_t cntl;
150725cf1a30Sjl 	int i;
150825cf1a30Sjl 
150925cf1a30Sjl 	flt_stat->mf_type = FLT_TYPE_PERMANENT_CE;
151025cf1a30Sjl 	/*
151125cf1a30Sjl 	 * rewrite request 1st time reads and correct error data
151225cf1a30Sjl 	 * and write to DIMM.  2nd rewrite request must be issued
151325cf1a30Sjl 	 * after REW_CE/UE/END is 0.  When the 2nd request is completed,
151425cf1a30Sjl 	 * if REW_CE = 1, then it is permanent CE.
151525cf1a30Sjl 	 */
151625cf1a30Sjl 	for (i = 0; i < 2; i++) {
1517*601c2e1eSdhain 		cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add, 0);
1518*601c2e1eSdhain 
1519*601c2e1eSdhain 		if (cntl == 0) {
1520*601c2e1eSdhain 			/* timeout case */
1521*601c2e1eSdhain 			return;
1522*601c2e1eSdhain 		}
152325cf1a30Sjl 		/*
152425cf1a30Sjl 		 * If the error becomes UE or CMPE
152525cf1a30Sjl 		 * we return to the caller immediately.
152625cf1a30Sjl 		 */
152725cf1a30Sjl 		if (cntl & MAC_CNTL_REW_UE) {
152825cf1a30Sjl 			if (ptrl_error)
152925cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE;
153025cf1a30Sjl 			else
153125cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_UE;
153225cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_UE;
153325cf1a30Sjl 			return;
153425cf1a30Sjl 		}
153525cf1a30Sjl 		if (cntl & MAC_CNTL_REW_CMPE) {
153625cf1a30Sjl 			if (ptrl_error)
153725cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE;
153825cf1a30Sjl 			else
153925cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE;
154025cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_CMPE;
154125cf1a30Sjl 			return;
154225cf1a30Sjl 		}
154325cf1a30Sjl 	}
154425cf1a30Sjl 	if (!(cntl & MAC_CNTL_REW_CE)) {
154525cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE;
154625cf1a30Sjl 	}
154725cf1a30Sjl 
154825cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
154925cf1a30Sjl 		/* report PERMANENT_CE to SP via SCF */
155025cf1a30Sjl 		if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) {
155125cf1a30Sjl 			mc_queue_scf_log(mcp, flt_stat, bank);
155225cf1a30Sjl 		}
155325cf1a30Sjl 	}
155425cf1a30Sjl }
155525cf1a30Sjl 
155625cf1a30Sjl #define	IS_CMPE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\
155725cf1a30Sjl 				MAC_CNTL_MI_CMPE))
155825cf1a30Sjl #define	IS_UE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE))
155925cf1a30Sjl #define	IS_CE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE))
156025cf1a30Sjl #define	IS_OK(cntl, f)	(!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \
156125cf1a30Sjl 			MAC_CNTL_MI_ERRS)))
156225cf1a30Sjl 
156325cf1a30Sjl 
156425cf1a30Sjl static int
156525cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error)
156625cf1a30Sjl {
156725cf1a30Sjl 	if (ptrl_error) {
156825cf1a30Sjl 		return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE);
156925cf1a30Sjl 	} else {
157025cf1a30Sjl 		return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE);
157125cf1a30Sjl 	}
157225cf1a30Sjl }
157325cf1a30Sjl 
157425cf1a30Sjl void
157525cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value)
157625cf1a30Sjl {
157737afe445Shyw 	int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
157837afe445Shyw 
157937afe445Shyw 	if (mcp->mc_speedup_period[ebank] > 0)
15800cc8ae86Sav 		value |= mc_max_speed;
15810cc8ae86Sav 	else
15820cc8ae86Sav 		value |= mcp->mc_speed;
158325cf1a30Sjl 	ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value);
158425cf1a30Sjl }
158525cf1a30Sjl 
158625cf1a30Sjl static void
158725cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
158825cf1a30Sjl {
158925cf1a30Sjl 	flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
1590d8a0cca9Swh 	    MAC_CNTL_PTRL_ERRS;
159125cf1a30Sjl 	flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank));
159225cf1a30Sjl 	flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank));
159325cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
1594aeb241b2Sav 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
159525cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
159625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
159725cf1a30Sjl }
159825cf1a30Sjl 
159925cf1a30Sjl static void
160025cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
160125cf1a30Sjl {
160225cf1a30Sjl 	uint32_t status, old_status;
160325cf1a30Sjl 
1604d8a0cca9Swh 	status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & MAC_CNTL_MI_ERRS;
160525cf1a30Sjl 	old_status = 0;
160625cf1a30Sjl 
160725cf1a30Sjl 	/* we keep reading until the status is stable */
160825cf1a30Sjl 	while (old_status != status) {
160925cf1a30Sjl 		old_status = status;
1610d8a0cca9Swh 		flt_stat->mf_err_add = LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank));
1611d8a0cca9Swh 		flt_stat->mf_err_log = LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank));
161225cf1a30Sjl 		status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
1613d8a0cca9Swh 		    MAC_CNTL_MI_ERRS;
161425cf1a30Sjl 		if (status == old_status) {
161525cf1a30Sjl 			break;
161625cf1a30Sjl 		}
161725cf1a30Sjl 	}
161825cf1a30Sjl 
161925cf1a30Sjl 	flt_stat->mf_cntl = status;
162025cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
1621aeb241b2Sav 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
162225cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
162325cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
162425cf1a30Sjl }
162525cf1a30Sjl 
162625cf1a30Sjl 
162725cf1a30Sjl /*
162825cf1a30Sjl  * Error philosophy for mirror mode:
162925cf1a30Sjl  *
163025cf1a30Sjl  * PTRL (The error address for both banks are same, since ptrl stops if it
163125cf1a30Sjl  * detects error.)
16321039f409Sav  * - Compare error  log CMPE.
163325cf1a30Sjl  *
163425cf1a30Sjl  * - UE-UE           Report MUE.  No rewrite.
163525cf1a30Sjl  *
163625cf1a30Sjl  * - UE-*	     UE-(CE/OK). Rewrite to scrub UE.  Report SUE.
163725cf1a30Sjl  *
163825cf1a30Sjl  * - CE-*            CE-(CE/OK). Scrub to determine if CE is permanent.
163925cf1a30Sjl  *                   If CE is permanent, inform SCF.  Once for each
164025cf1a30Sjl  *		     Dimm.  If CE becomes UE or CMPE, go back to above.
164125cf1a30Sjl  *
164225cf1a30Sjl  *
164325cf1a30Sjl  * MI (The error addresses for each bank are the same or different.)
16441039f409Sav  * - Compare  error  If addresses are the same.  Just CMPE, so log CMPE.
164525cf1a30Sjl  *		     If addresses are different (this could happen
16461039f409Sav  *		     as a result of scrubbing.  Report each separately.
164725cf1a30Sjl  *		     Only report error info on each side.
164825cf1a30Sjl  *
164925cf1a30Sjl  * - UE-UE           Addresses are the same.  Report MUE.
165025cf1a30Sjl  *		     Addresses are different.  Report SUE on each bank.
165125cf1a30Sjl  *		     Rewrite to clear UE.
165225cf1a30Sjl  *
165325cf1a30Sjl  * - UE-*	     UE-(CE/OK)
165425cf1a30Sjl  *		     Rewrite to clear UE.  Report SUE for the bank.
165525cf1a30Sjl  *
165625cf1a30Sjl  * - CE-*            CE-(CE/OK).  Scrub to determine if CE is permanent.
165725cf1a30Sjl  *                   If CE becomes UE or CMPE, go back to above.
165825cf1a30Sjl  *
165925cf1a30Sjl  */
166025cf1a30Sjl 
166125cf1a30Sjl static int
166225cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat)
166325cf1a30Sjl {
166425cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
166525cf1a30Sjl 	int i;
166625cf1a30Sjl 	int rv = 0;
1667*601c2e1eSdhain 	int bank;
1668*601c2e1eSdhain 	int rewrite_timeout = 0;
166925cf1a30Sjl 
167025cf1a30Sjl 	MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n",
1671d8a0cca9Swh 	    flt_stat[0].mf_cntl, flt_stat[1].mf_cntl);
167225cf1a30Sjl 
167325cf1a30Sjl 	if (ptrl_error) {
1674d8a0cca9Swh 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) &
1675d8a0cca9Swh 		    MAC_CNTL_PTRL_ERRS) == 0)
167625cf1a30Sjl 			return (0);
167725cf1a30Sjl 	} else {
1678d8a0cca9Swh 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) &
1679d8a0cca9Swh 		    MAC_CNTL_MI_ERRS) == 0)
168025cf1a30Sjl 			return (0);
168125cf1a30Sjl 	}
168225cf1a30Sjl 
168325cf1a30Sjl 	/*
168425cf1a30Sjl 	 * First we take care of the case of CE
168525cf1a30Sjl 	 * because they can become UE or CMPE
168625cf1a30Sjl 	 */
168725cf1a30Sjl 	for (i = 0; i < 2; i++) {
168825cf1a30Sjl 		if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) {
1689*601c2e1eSdhain 			bank = flt_stat[i].mf_flt_maddr.ma_bank;
1690*601c2e1eSdhain 			MC_LOG("CE detected on bank %d\n", bank);
1691*601c2e1eSdhain 			mc_scrub_ce(mcp, bank, &flt_stat[i], ptrl_error);
1692*601c2e1eSdhain 			if (MC_REWRITE_ACTIVE(mcp, bank)) {
1693*601c2e1eSdhain 				rewrite_timeout = 1;
1694*601c2e1eSdhain 			}
169525cf1a30Sjl 			rv = 1;
169625cf1a30Sjl 		}
169725cf1a30Sjl 	}
169825cf1a30Sjl 
1699*601c2e1eSdhain 	if (rewrite_timeout)
1700*601c2e1eSdhain 		return (0);
1701*601c2e1eSdhain 
170225cf1a30Sjl 	/* The above scrubbing can turn CE into UE or CMPE */
170325cf1a30Sjl 
170425cf1a30Sjl 	/*
170525cf1a30Sjl 	 * Now we distinguish two cases: same address or not
170625cf1a30Sjl 	 * the same address.  It might seem more intuitive to
170725cf1a30Sjl 	 * distinguish PTRL v.s. MI error but it is more
170825cf1a30Sjl 	 * complicated that way.
170925cf1a30Sjl 	 */
171025cf1a30Sjl 
171125cf1a30Sjl 	if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) {
171225cf1a30Sjl 
171325cf1a30Sjl 		if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) ||
171425cf1a30Sjl 		    IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) {
171525cf1a30Sjl 			flt_stat[0].mf_type = FLT_TYPE_CMPE;
171625cf1a30Sjl 			flt_stat[1].mf_type = FLT_TYPE_CMPE;
171725cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
171825cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
171925cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
172025cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
172125cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
17221039f409Sav 			/*
17231039f409Sav 			 * Compare error is result of MAC internal error, so
17241039f409Sav 			 * simply log it instead of publishing an ereport. SCF
17251039f409Sav 			 * diagnoses all the MAC internal and its i/f error.
17261039f409Sav 			 * mc_err_drain(mc_aflt);
17271039f409Sav 			 */
17281039f409Sav 			MC_LOG("cmpe error detected\n");
172925cf1a30Sjl 			return (1);
173025cf1a30Sjl 		}
173125cf1a30Sjl 
173225cf1a30Sjl 		if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) &&
1733d8a0cca9Swh 		    IS_UE(flt_stat[1].mf_cntl, ptrl_error)) {
173425cf1a30Sjl 			/* Both side are UE's */
173525cf1a30Sjl 
173625cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[0]);
173725cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[1]);
173825cf1a30Sjl 			MC_LOG("MUE detected\n");
17390cc8ae86Sav 			flt_stat[0].mf_type = FLT_TYPE_MUE;
17400cc8ae86Sav 			flt_stat[1].mf_type = FLT_TYPE_MUE;
174125cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_MUE;
174225cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
174325cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
174425cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
174525cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
174625cf1a30Sjl 			mc_err_drain(mc_aflt);
174725cf1a30Sjl 			return (1);
174825cf1a30Sjl 		}
174925cf1a30Sjl 
175025cf1a30Sjl 		/* Now the only case is UE/CE, UE/OK, or don't care */
175125cf1a30Sjl 		for (i = 0; i < 2; i++) {
1752*601c2e1eSdhain 			if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
17530cc8ae86Sav 
17540cc8ae86Sav 			/* rewrite can clear the one side UE error */
17550cc8ae86Sav 
175625cf1a30Sjl 			if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) {
175725cf1a30Sjl 				(void) do_rewrite(mcp,
175825cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_bank,
1759*601c2e1eSdhain 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr, 0);
176025cf1a30Sjl 			}
176125cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
176225cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
176325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
176425cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
176525cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
176625cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
176725cf1a30Sjl 			mc_err_drain(mc_aflt);
176825cf1a30Sjl 			/* Once we hit a UE/CE or UE/OK case, done */
176925cf1a30Sjl 			return (1);
1770*601c2e1eSdhain 			}
177125cf1a30Sjl 		}
177225cf1a30Sjl 
177325cf1a30Sjl 	} else {
177425cf1a30Sjl 		/*
177525cf1a30Sjl 		 * addresses are different. That means errors
177625cf1a30Sjl 		 * on the 2 banks are not related at all.
177725cf1a30Sjl 		 */
177825cf1a30Sjl 		for (i = 0; i < 2; i++) {
1779d8a0cca9Swh 			if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) {
1780d8a0cca9Swh 				flt_stat[i].mf_type = FLT_TYPE_CMPE;
1781d8a0cca9Swh 				mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
1782d8a0cca9Swh 				mc_aflt->mflt_nflts = 1;
1783d8a0cca9Swh 				mc_aflt->mflt_stat[0] = &flt_stat[i];
1784d8a0cca9Swh 				mc_aflt->mflt_pr = PR_UE;
1785d8a0cca9Swh 				/*
1786d8a0cca9Swh 				 * Compare error is result of MAC internal
1787d8a0cca9Swh 				 * error, so simply log it instead of
1788d8a0cca9Swh 				 * publishing an ereport. SCF diagnoses all
1789d8a0cca9Swh 				 * the MAC internal and its interface error.
1790d8a0cca9Swh 				 * mc_err_drain(mc_aflt);
1791d8a0cca9Swh 				 */
1792d8a0cca9Swh 				MC_LOG("cmpe error detected\n");
1793d8a0cca9Swh 				/* no more report on this bank */
1794d8a0cca9Swh 				flt_stat[i].mf_cntl = 0;
1795d8a0cca9Swh 				rv = 1;
1796d8a0cca9Swh 			}
179725cf1a30Sjl 		}
179825cf1a30Sjl 
17990cc8ae86Sav 		/* rewrite can clear the one side UE error */
18000cc8ae86Sav 
180125cf1a30Sjl 		for (i = 0; i < 2; i++) {
1802d8a0cca9Swh 			if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
1803d8a0cca9Swh 				(void) do_rewrite(mcp,
1804d8a0cca9Swh 				    flt_stat[i].mf_flt_maddr.ma_bank,
1805*601c2e1eSdhain 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr,
1806*601c2e1eSdhain 				    0);
1807d8a0cca9Swh 				flt_stat[i].mf_type = FLT_TYPE_UE;
1808d8a0cca9Swh 				MAC_SET_ERRLOG_INFO(&flt_stat[i]);
1809d8a0cca9Swh 				mc_aflt->mflt_erpt_class = MC_OPL_SUE;
1810d8a0cca9Swh 				mc_aflt->mflt_stat[0] = &flt_stat[i];
1811d8a0cca9Swh 				mc_aflt->mflt_nflts = 1;
1812d8a0cca9Swh 				mc_aflt->mflt_pr = PR_MCE;
1813d8a0cca9Swh 				mc_err_drain(mc_aflt);
1814d8a0cca9Swh 				rv = 1;
1815d8a0cca9Swh 			}
181625cf1a30Sjl 		}
181725cf1a30Sjl 	}
181825cf1a30Sjl 	return (rv);
181925cf1a30Sjl }
182025cf1a30Sjl static void
1821738dd194Shyw mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
182225cf1a30Sjl {
182325cf1a30Sjl 	mc_aflt_t mc_aflt;
182425cf1a30Sjl 	mc_flt_stat_t flt_stat[2], mi_flt_stat[2];
18250cc8ae86Sav 	int i;
18260cc8ae86Sav 	int mi_valid;
182725cf1a30Sjl 
1828738dd194Shyw 	ASSERT(rsaddr);
1829738dd194Shyw 
183025cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
183125cf1a30Sjl 	bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t));
183225cf1a30Sjl 	bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t));
183325cf1a30Sjl 
1834ad59b69dSbm 
183525cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
183625cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
183725cf1a30Sjl 
183825cf1a30Sjl 	/* Now read all the registers into flt_stat */
183925cf1a30Sjl 
18400cc8ae86Sav 	for (i = 0; i < 2; i++) {
18410cc8ae86Sav 		MC_LOG("Reading registers of bank %d\n", bank);
18420cc8ae86Sav 		/* patrol registers */
18430cc8ae86Sav 		mc_read_ptrl_reg(mcp, bank, &flt_stat[i]);
184425cf1a30Sjl 
1845738dd194Shyw 		/*
1846738dd194Shyw 		 * In mirror mode, it is possible that only one bank
1847738dd194Shyw 		 * may report the error. We need to check for it to
1848738dd194Shyw 		 * ensure we pick the right addr value for patrol restart.
1849738dd194Shyw 		 * Note that if both banks reported errors, we pick the
1850738dd194Shyw 		 * 2nd one. Both banks should reported the same error address.
1851738dd194Shyw 		 */
1852738dd194Shyw 		if (flt_stat[i].mf_cntl & MAC_CNTL_PTRL_ERRS)
1853738dd194Shyw 			rsaddr->mi_restartaddr = flt_stat[i].mf_flt_maddr;
185425cf1a30Sjl 
18550cc8ae86Sav 		MC_LOG("ptrl registers cntl %x add %x log %x\n",
1856d8a0cca9Swh 		    flt_stat[i].mf_cntl, flt_stat[i].mf_err_add,
1857d8a0cca9Swh 		    flt_stat[i].mf_err_log);
185825cf1a30Sjl 
18590cc8ae86Sav 		/* MI registers */
18600cc8ae86Sav 		mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]);
186125cf1a30Sjl 
18620cc8ae86Sav 		MC_LOG("MI registers cntl %x add %x log %x\n",
1863d8a0cca9Swh 		    mi_flt_stat[i].mf_cntl, mi_flt_stat[i].mf_err_add,
1864d8a0cca9Swh 		    mi_flt_stat[i].mf_err_log);
186525cf1a30Sjl 
18660cc8ae86Sav 		bank = bank^1;
18670cc8ae86Sav 	}
186825cf1a30Sjl 
186925cf1a30Sjl 	/* clear errors once we read all the registers */
1870d8a0cca9Swh 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
187125cf1a30Sjl 
18720cc8ae86Sav 	MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
18730cc8ae86Sav 
18740cc8ae86Sav 	/* Process MI errors first */
187525cf1a30Sjl 
18760cc8ae86Sav 	/* if not error mode, cntl1 is 0 */
18770cc8ae86Sav 	if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
1878d8a0cca9Swh 	    (mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
18790cc8ae86Sav 		mi_flt_stat[0].mf_cntl = 0;
18800cc8ae86Sav 
18810cc8ae86Sav 	if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
1882d8a0cca9Swh 	    (mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
18830cc8ae86Sav 		mi_flt_stat[1].mf_cntl = 0;
188425cf1a30Sjl 
18850cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
18860cc8ae86Sav 	mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]);
18870cc8ae86Sav 
18880cc8ae86Sav 	if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1889d8a0cca9Swh 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[0].mf_cntl &
1890d8a0cca9Swh 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
1891d8a0cca9Swh 	    (flt_stat[0].mf_err_add == mi_flt_stat[0].mf_err_add) &&
1892d8a0cca9Swh 	    (((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1893d8a0cca9Swh 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[1].mf_cntl &
1894d8a0cca9Swh 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
1895d8a0cca9Swh 	    (flt_stat[1].mf_err_add == mi_flt_stat[1].mf_err_add)) {
18960cc8ae86Sav #ifdef DEBUG
18970cc8ae86Sav 		MC_LOG("discarding PTRL error because "
18980cc8ae86Sav 		    "it is the same as MI\n");
18990cc8ae86Sav #endif
1900738dd194Shyw 		rsaddr->mi_valid = mi_valid;
19010cc8ae86Sav 		return;
19020cc8ae86Sav 	}
190325cf1a30Sjl 	/* if not error mode, cntl1 is 0 */
190425cf1a30Sjl 	if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
1905d8a0cca9Swh 	    (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
190625cf1a30Sjl 		flt_stat[0].mf_cntl = 0;
190725cf1a30Sjl 
190825cf1a30Sjl 	if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
1909d8a0cca9Swh 	    (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
191025cf1a30Sjl 		flt_stat[1].mf_cntl = 0;
191125cf1a30Sjl 
191225cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
1913738dd194Shyw 	rsaddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]);
191425cf1a30Sjl }
191525cf1a30Sjl static int
191625cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt,
191725cf1a30Sjl 	mc_flt_stat_t *flt_stat)
191825cf1a30Sjl {
191925cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
192025cf1a30Sjl 	int rv = 0;
192125cf1a30Sjl 
192225cf1a30Sjl 	mc_aflt->mflt_erpt_class = NULL;
192325cf1a30Sjl 	if (IS_UE(flt_stat->mf_cntl, ptrl_error)) {
19241039f409Sav 		MC_LOG("UE detected\n");
192525cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_UE;
192625cf1a30Sjl 		mc_aflt->mflt_erpt_class = MC_OPL_UE;
192725cf1a30Sjl 		mc_aflt->mflt_pr = PR_UE;
192825cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
192925cf1a30Sjl 		rv = 1;
193025cf1a30Sjl 	} else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) {
19311039f409Sav 		MC_LOG("CE detected\n");
193225cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
193325cf1a30Sjl 
19341039f409Sav 		/* Error type can change after scrubbing */
193525cf1a30Sjl 		mc_scrub_ce(mcp, bank, flt_stat, ptrl_error);
1936*601c2e1eSdhain 		if (MC_REWRITE_ACTIVE(mcp, bank)) {
1937*601c2e1eSdhain 			return (0);
1938*601c2e1eSdhain 		}
193925cf1a30Sjl 
1940056c948bStsien 		if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE) {
1941056c948bStsien 			mc_aflt->mflt_erpt_class = MC_OPL_ICE;
1942056c948bStsien 			mc_aflt->mflt_pr = PR_MCE;
1943056c948bStsien 		} else if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
194425cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CE;
194525cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
194625cf1a30Sjl 		} else if (flt_stat->mf_type == FLT_TYPE_UE) {
194725cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_UE;
194825cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
194925cf1a30Sjl 		}
195025cf1a30Sjl 		rv = 1;
195125cf1a30Sjl 	}
1952d8a0cca9Swh 	MC_LOG("mc_process_error: fault type %x erpt %s\n", flt_stat->mf_type,
1953d8a0cca9Swh 	    mc_aflt->mflt_erpt_class);
195425cf1a30Sjl 	if (mc_aflt->mflt_erpt_class) {
195525cf1a30Sjl 		mc_aflt->mflt_stat[0] = flt_stat;
195625cf1a30Sjl 		mc_aflt->mflt_nflts = 1;
195725cf1a30Sjl 		mc_err_drain(mc_aflt);
195825cf1a30Sjl 	}
195925cf1a30Sjl 	return (rv);
196025cf1a30Sjl }
196125cf1a30Sjl 
196225cf1a30Sjl static void
1963738dd194Shyw mc_error_handler(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
196425cf1a30Sjl {
196525cf1a30Sjl 	mc_aflt_t mc_aflt;
196625cf1a30Sjl 	mc_flt_stat_t flt_stat, mi_flt_stat;
19670cc8ae86Sav 	int mi_valid;
196825cf1a30Sjl 
196925cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
197025cf1a30Sjl 	bzero(&flt_stat, sizeof (mc_flt_stat_t));
197125cf1a30Sjl 	bzero(&mi_flt_stat, sizeof (mc_flt_stat_t));
197225cf1a30Sjl 
197325cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
197425cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
197525cf1a30Sjl 
197625cf1a30Sjl 	/* patrol registers */
197725cf1a30Sjl 	mc_read_ptrl_reg(mcp, bank, &flt_stat);
197825cf1a30Sjl 
1979738dd194Shyw 	ASSERT(rsaddr);
1980738dd194Shyw 	rsaddr->mi_restartaddr = flt_stat.mf_flt_maddr;
198125cf1a30Sjl 
1982d8a0cca9Swh 	MC_LOG("ptrl registers cntl %x add %x log %x\n", flt_stat.mf_cntl,
1983d8a0cca9Swh 	    flt_stat.mf_err_add, flt_stat.mf_err_log);
198425cf1a30Sjl 
198525cf1a30Sjl 	/* MI registers */
198625cf1a30Sjl 	mc_read_mi_reg(mcp, bank, &mi_flt_stat);
198725cf1a30Sjl 
19880cc8ae86Sav 
1989d8a0cca9Swh 	MC_LOG("MI registers cntl %x add %x log %x\n", mi_flt_stat.mf_cntl,
1990d8a0cca9Swh 	    mi_flt_stat.mf_err_add, mi_flt_stat.mf_err_log);
199125cf1a30Sjl 
199225cf1a30Sjl 	/* clear errors once we read all the registers */
199325cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
199425cf1a30Sjl 
19950cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
19960cc8ae86Sav 	if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) &&
1997d8a0cca9Swh 	    ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
1998d8a0cca9Swh 	    ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
19990cc8ae86Sav 		mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat);
20000cc8ae86Sav 	}
20010cc8ae86Sav 
20020cc8ae86Sav 	if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >>
2003d8a0cca9Swh 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat.mf_cntl &
2004d8a0cca9Swh 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
2005d8a0cca9Swh 	    (flt_stat.mf_err_add == mi_flt_stat.mf_err_add)) {
20060cc8ae86Sav #ifdef DEBUG
20070cc8ae86Sav 		MC_LOG("discarding PTRL error because "
20080cc8ae86Sav 		    "it is the same as MI\n");
20090cc8ae86Sav #endif
2010738dd194Shyw 		rsaddr->mi_valid = mi_valid;
20110cc8ae86Sav 		return;
20120cc8ae86Sav 	}
20130cc8ae86Sav 
201425cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
201525cf1a30Sjl 	if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) &&
2016d8a0cca9Swh 	    ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
2017d8a0cca9Swh 	    ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
2018d8a0cca9Swh 		rsaddr->mi_valid = mc_process_error(mcp, bank, &mc_aflt,
2019d8a0cca9Swh 		    &flt_stat);
202025cf1a30Sjl 	}
202125cf1a30Sjl }
202225cf1a30Sjl /*
202325cf1a30Sjl  *	memory patrol error handling algorithm:
202425cf1a30Sjl  *	timeout() is used to do periodic polling
202525cf1a30Sjl  *	This is the flow chart.
202625cf1a30Sjl  *	timeout ->
202725cf1a30Sjl  *	mc_check_errors()
202825cf1a30Sjl  *	    if memory bank is installed, read the status register
202925cf1a30Sjl  *	    if any error bit is set,
203025cf1a30Sjl  *	    -> mc_error_handler()
20311039f409Sav  *		-> read all error registers
203225cf1a30Sjl  *	        -> mc_process_error()
203325cf1a30Sjl  *	            determine error type
203425cf1a30Sjl  *	            rewrite to clear error or scrub to determine CE type
203525cf1a30Sjl  *	            inform SCF on permanent CE
203625cf1a30Sjl  *	        -> mc_err_drain
203725cf1a30Sjl  *	            page offline processing
203825cf1a30Sjl  *	            -> mc_ereport_post()
203925cf1a30Sjl  */
204025cf1a30Sjl 
2041*601c2e1eSdhain static void
2042*601c2e1eSdhain mc_process_rewrite(mc_opl_t *mcp, int bank)
2043*601c2e1eSdhain {
2044*601c2e1eSdhain 	uint32_t rew_addr, cntl;
2045*601c2e1eSdhain 	mc_retry_info_t *retry;
2046*601c2e1eSdhain 	struct mc_bank *bankp;
2047*601c2e1eSdhain 
2048*601c2e1eSdhain 	bankp = &(mcp->mc_bank[bank]);
2049*601c2e1eSdhain 	retry = bankp->mcb_active;
2050*601c2e1eSdhain 	if (retry == NULL)
2051*601c2e1eSdhain 		return;
2052*601c2e1eSdhain 
2053*601c2e1eSdhain 	if (retry->ri_state <= RETRY_STATE_ACTIVE) {
2054*601c2e1eSdhain 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
2055*601c2e1eSdhain 		if (cntl & MAC_CNTL_PTRL_STATUS)
2056*601c2e1eSdhain 			return;
2057*601c2e1eSdhain 		rew_addr = retry->ri_addr;
2058*601c2e1eSdhain 		ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), rew_addr);
2059*601c2e1eSdhain 		MAC_REW_REQ(mcp, bank);
2060*601c2e1eSdhain 
2061*601c2e1eSdhain 		retry->ri_state = RETRY_STATE_REWRITE;
2062*601c2e1eSdhain 	}
2063*601c2e1eSdhain 
2064*601c2e1eSdhain 	cntl = ldphysio(MAC_PTRL_CNTL(mcp, bank));
2065*601c2e1eSdhain 
2066*601c2e1eSdhain 	if (cntl & MAC_CNTL_REW_END) {
2067*601c2e1eSdhain 		MAC_CLEAR_ERRS(mcp, bank,
2068*601c2e1eSdhain 		    MAC_CNTL_REW_ERRS);
2069*601c2e1eSdhain 		mc_clear_rewrite(mcp, bank);
2070*601c2e1eSdhain 	} else {
2071*601c2e1eSdhain 		/*
2072*601c2e1eSdhain 		 * If the rewrite does not complete in
2073*601c2e1eSdhain 		 * 1 hour, we have to consider this a HW
2074*601c2e1eSdhain 		 * failure.  However, there is no recovery
2075*601c2e1eSdhain 		 * mechanism.  The only thing we can do
2076*601c2e1eSdhain 		 * to to print a warning message to the
2077*601c2e1eSdhain 		 * console.  We continue to increment the
2078*601c2e1eSdhain 		 * counter but we only print the message
2079*601c2e1eSdhain 		 * once.  It will take the counter a long
2080*601c2e1eSdhain 		 * time to wrap around and the user might
2081*601c2e1eSdhain 		 * see a second message.  In practice,
2082*601c2e1eSdhain 		 * we have never hit this condition but
2083*601c2e1eSdhain 		 * we have to keep the code here just in case.
2084*601c2e1eSdhain 		 */
2085*601c2e1eSdhain 		if (++mcp->mc_bank[bank].mcb_rewrite_count
2086*601c2e1eSdhain 		    == mc_max_rewrite_retry) {
2087*601c2e1eSdhain 			cmn_err(CE_WARN, "Memory patrol feature is"
2088*601c2e1eSdhain 			" partly suspended on /LSB%d/B%d"
2089*601c2e1eSdhain 			" due to heavy memory load,"
2090*601c2e1eSdhain 			" and it will restart"
2091*601c2e1eSdhain 			" automatically.\n", mcp->mc_board_num,
2092*601c2e1eSdhain 			    bank);
2093*601c2e1eSdhain 		}
2094*601c2e1eSdhain 	}
2095*601c2e1eSdhain }
2096*601c2e1eSdhain 
209725cf1a30Sjl static void
209825cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp)
209925cf1a30Sjl {
2100738dd194Shyw 	mc_rsaddr_info_t rsaddr_info;
210125cf1a30Sjl 	int i, error_count = 0;
210225cf1a30Sjl 	uint32_t stat, cntl;
21030cc8ae86Sav 	int running;
2104cfb9e062Shyw 	int wrapped;
210537afe445Shyw 	int ebk;
210625cf1a30Sjl 
210725cf1a30Sjl 	/*
210825cf1a30Sjl 	 * scan errors.
210925cf1a30Sjl 	 */
21100cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS)
21110cc8ae86Sav 		return;
21120cc8ae86Sav 
211325cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
211425cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
2115*601c2e1eSdhain 			if (MC_REWRITE_ACTIVE(mcp, i)) {
2116*601c2e1eSdhain 				mc_process_rewrite(mcp, i);
2117*601c2e1eSdhain 			}
211825cf1a30Sjl 			stat = ldphysio(MAC_PTRL_STAT(mcp, i));
211925cf1a30Sjl 			cntl = ldphysio(MAC_PTRL_CNTL(mcp, i));
21200cc8ae86Sav 			running = cntl & MAC_CNTL_PTRL_START;
2121cfb9e062Shyw 			wrapped = cntl & MAC_CNTL_PTRL_ADD_MAX;
21220cc8ae86Sav 
212337afe445Shyw 			/* Compute the effective bank idx */
212437afe445Shyw 			ebk = (IS_MIRROR(mcp, i)) ? MIRROR_IDX(i) : i;
212537afe445Shyw 
2126cfb9e062Shyw 			if (mc_debug_show_all || stat) {
2127cfb9e062Shyw 				MC_LOG("/LSB%d/B%d stat %x cntl %x\n",
2128d8a0cca9Swh 				    mcp->mc_board_num, i, stat, cntl);
2129cfb9e062Shyw 			}
2130cfb9e062Shyw 
2131cfb9e062Shyw 			/*
2132cfb9e062Shyw 			 * Update stats and reset flag if the HW patrol
2133cfb9e062Shyw 			 * wrapped around in its scan.
2134cfb9e062Shyw 			 */
2135cfb9e062Shyw 			if (wrapped) {
213625cf1a30Sjl 				MAC_CLEAR_MAX(mcp, i);
213737afe445Shyw 				mcp->mc_period[ebk]++;
213837afe445Shyw 				if (IS_MIRROR(mcp, i))
2139d8a0cca9Swh 					MC_LOG("mirror mc period %ld on "
2140d8a0cca9Swh 					    "/LSB%d/B%d\n", mcp->mc_period[ebk],
2141d8a0cca9Swh 					    mcp->mc_board_num, i);
214237afe445Shyw 				else {
2143d8a0cca9Swh 					MC_LOG("mc period %ld on "
2144d8a0cca9Swh 					    "/LSB%d/B%d\n", mcp->mc_period[ebk],
2145d8a0cca9Swh 					    mcp->mc_board_num, i);
214637afe445Shyw 				}
214725cf1a30Sjl 			}
2148cfb9e062Shyw 
2149cfb9e062Shyw 			if (running) {
2150cfb9e062Shyw 				/*
2151cfb9e062Shyw 				 * Mac patrol HW is still running.
2152cfb9e062Shyw 				 * Normally when an error is detected,
2153cfb9e062Shyw 				 * the HW patrol will stop so that we
2154cfb9e062Shyw 				 * can collect error data for reporting.
2155cfb9e062Shyw 				 * Certain errors (MI errors) detected may not
2156cfb9e062Shyw 				 * cause the HW patrol to stop which is a
2157cfb9e062Shyw 				 * problem since we cannot read error data while
2158cfb9e062Shyw 				 * the HW patrol is running. SW is not allowed
2159cfb9e062Shyw 				 * to stop the HW patrol while it is running
2160cfb9e062Shyw 				 * as it may cause HW inconsistency. This is
2161cfb9e062Shyw 				 * described in a HW errata.
2162cfb9e062Shyw 				 * In situations where we detected errors
2163cfb9e062Shyw 				 * that may not cause the HW patrol to stop.
2164cfb9e062Shyw 				 * We speed up the HW patrol scanning in
2165cfb9e062Shyw 				 * the hope that it will find the 'real' PTRL
2166cfb9e062Shyw 				 * errors associated with the previous errors
2167cfb9e062Shyw 				 * causing the HW to finally stop so that we
2168cfb9e062Shyw 				 * can do the reporting.
2169cfb9e062Shyw 				 */
2170cfb9e062Shyw 				/*
2171cfb9e062Shyw 				 * Check to see if we did speed up
2172cfb9e062Shyw 				 * the HW patrol due to previous errors
2173cfb9e062Shyw 				 * detected that did not cause the patrol
2174cfb9e062Shyw 				 * to stop. We only do it if HW patrol scan
2175cfb9e062Shyw 				 * wrapped (counted as completing a 'period').
2176cfb9e062Shyw 				 */
217737afe445Shyw 				if (mcp->mc_speedup_period[ebk] > 0) {
2178d8a0cca9Swh 					if (wrapped &&
2179d8a0cca9Swh 					    (--mcp->mc_speedup_period[ebk] ==
2180d8a0cca9Swh 					    0)) {
2181d8a0cca9Swh 						/*
2182d8a0cca9Swh 						 * We did try to speed up.
2183d8a0cca9Swh 						 * The speed up period has
2184d8a0cca9Swh 						 * expired and the HW patrol
2185d8a0cca9Swh 						 * is still running.  The
2186d8a0cca9Swh 						 * errors must be intermittent.
2187d8a0cca9Swh 						 * We have no choice but to
2188d8a0cca9Swh 						 * ignore them, reset the scan
2189d8a0cca9Swh 						 * speed to normal and clear
2190d8a0cca9Swh 						 * the MI error bits. For
2191d8a0cca9Swh 						 * mirror mode, we need to
2192d8a0cca9Swh 						 * clear errors on both banks.
2193d8a0cca9Swh 						 */
2194d8a0cca9Swh 						MC_LOG("Clearing MI errors\n");
2195d8a0cca9Swh 						MAC_CLEAR_ERRS(mcp, i,
2196d8a0cca9Swh 						    MAC_CNTL_MI_ERRS);
2197d8a0cca9Swh 
2198d8a0cca9Swh 						if (IS_MIRROR(mcp, i)) {
2199d8a0cca9Swh 							MC_LOG("Clearing "
2200d8a0cca9Swh 							    "Mirror MI errs\n");
2201d8a0cca9Swh 							MAC_CLEAR_ERRS(mcp,
2202d8a0cca9Swh 							    i^1,
2203d8a0cca9Swh 							    MAC_CNTL_MI_ERRS);
2204d8a0cca9Swh 						}
220537afe445Shyw 					}
2206cfb9e062Shyw 				} else if (stat & MAC_STAT_MI_ERRS) {
2207cfb9e062Shyw 					/*
2208cfb9e062Shyw 					 * MI errors detected but we cannot
2209cfb9e062Shyw 					 * report them since the HW patrol
2210cfb9e062Shyw 					 * is still running.
2211cfb9e062Shyw 					 * We will attempt to speed up the
2212cfb9e062Shyw 					 * scanning and hopefully the HW
2213cfb9e062Shyw 					 * can detect PRTL errors at the same
2214cfb9e062Shyw 					 * location that cause the HW patrol
2215cfb9e062Shyw 					 * to stop.
2216cfb9e062Shyw 					 */
221737afe445Shyw 					mcp->mc_speedup_period[ebk] = 2;
22180cc8ae86Sav 					MAC_CMD(mcp, i, 0);
22190cc8ae86Sav 				}
2220cfb9e062Shyw 			} else if (stat & (MAC_STAT_PTRL_ERRS |
2221cfb9e062Shyw 			    MAC_STAT_MI_ERRS)) {
2222cfb9e062Shyw 				/*
2223cfb9e062Shyw 				 * HW Patrol has stopped and we found errors.
2224cfb9e062Shyw 				 * Proceed to collect and report error info.
2225cfb9e062Shyw 				 */
222637afe445Shyw 				mcp->mc_speedup_period[ebk] = 0;
2227738dd194Shyw 				rsaddr_info.mi_valid = 0;
2228738dd194Shyw 				rsaddr_info.mi_injectrestart = 0;
2229738dd194Shyw 				if (IS_MIRROR(mcp, i)) {
2230d8a0cca9Swh 					mc_error_handler_mir(mcp, i,
2231d8a0cca9Swh 					    &rsaddr_info);
2232738dd194Shyw 				} else {
2233d8a0cca9Swh 					mc_error_handler(mcp, i, &rsaddr_info);
2234738dd194Shyw 				}
2235cfb9e062Shyw 
2236cfb9e062Shyw 				error_count++;
2237738dd194Shyw 				restart_patrol(mcp, i, &rsaddr_info);
223825cf1a30Sjl 			} else {
2239cfb9e062Shyw 				/*
2240cfb9e062Shyw 				 * HW patrol scan has apparently stopped
2241cfb9e062Shyw 				 * but no errors detected/flagged.
2242cfb9e062Shyw 				 * Restart the HW patrol just to be sure.
224337afe445Shyw 				 * In mirror mode, the odd bank might have
224437afe445Shyw 				 * reported errors that caused the patrol to
224537afe445Shyw 				 * stop. We'll defer the restart to the odd
224637afe445Shyw 				 * bank in this case.
2247cfb9e062Shyw 				 */
224837afe445Shyw 				if (!IS_MIRROR(mcp, i) || (i & 0x1))
224937afe445Shyw 					restart_patrol(mcp, i, NULL);
225025cf1a30Sjl 			}
225125cf1a30Sjl 		}
225225cf1a30Sjl 	}
225325cf1a30Sjl 	if (error_count > 0)
225425cf1a30Sjl 		mcp->mc_last_error += error_count;
225525cf1a30Sjl 	else
225625cf1a30Sjl 		mcp->mc_last_error = 0;
225725cf1a30Sjl }
225825cf1a30Sjl 
22590cc8ae86Sav /*
22600cc8ae86Sav  * mc_polling -- Check errors for only one instance,
22610cc8ae86Sav  * but process errors for all instances to make sure we drain the errors
22620cc8ae86Sav  * faster than they can be accumulated.
22630cc8ae86Sav  *
22640cc8ae86Sav  * Polling on each board should be done only once per each
22650cc8ae86Sav  * mc_patrol_interval_sec.  This is equivalent to setting mc_tick_left
22660cc8ae86Sav  * to OPL_MAX_BOARDS and decrement by 1 on each timeout.
22670cc8ae86Sav  * Once mc_tick_left becomes negative, the board becomes a candidate
22680cc8ae86Sav  * for polling because it has waited for at least
22690cc8ae86Sav  * mc_patrol_interval_sec's long.    If mc_timeout_period is calculated
22701039f409Sav  * differently, this has to be updated accordingly.
22710cc8ae86Sav  */
227225cf1a30Sjl 
227325cf1a30Sjl static void
22740cc8ae86Sav mc_polling(void)
227525cf1a30Sjl {
22760cc8ae86Sav 	int i, scan_error;
22770cc8ae86Sav 	mc_opl_t *mcp;
227825cf1a30Sjl 
227925cf1a30Sjl 
22800cc8ae86Sav 	scan_error = 1;
22810cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
22820cc8ae86Sav 		mutex_enter(&mcmutex);
22830cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL) {
22840cc8ae86Sav 			mutex_exit(&mcmutex);
22850cc8ae86Sav 			continue;
22860cc8ae86Sav 		}
22870cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
22880cc8ae86Sav 		mutex_exit(&mcmutex);
2289738dd194Shyw 		if (!(mcp->mc_status & MC_POLL_RUNNING)) {
2290738dd194Shyw 			mutex_exit(&mcp->mc_lock);
2291738dd194Shyw 			continue;
2292738dd194Shyw 		}
22930cc8ae86Sav 		if (scan_error && mcp->mc_tick_left <= 0) {
22940cc8ae86Sav 			mc_check_errors_func((void *)mcp);
22950cc8ae86Sav 			mcp->mc_tick_left = OPL_MAX_BOARDS;
22960cc8ae86Sav 			scan_error = 0;
22970cc8ae86Sav 		} else {
22980cc8ae86Sav 			mcp->mc_tick_left--;
22990cc8ae86Sav 		}
23000cc8ae86Sav 		mc_process_scf_log(mcp);
23010cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
230225cf1a30Sjl 	}
230325cf1a30Sjl }
230425cf1a30Sjl 
230525cf1a30Sjl static void
230625cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr)
230725cf1a30Sjl {
230825cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
230925cf1a30Sjl 	maddr->ma_bank = bank;
231025cf1a30Sjl 	maddr->ma_dimm_addr = 0;
231125cf1a30Sjl }
231225cf1a30Sjl 
231325cf1a30Sjl typedef struct mc_mem_range {
231425cf1a30Sjl 	uint64_t	addr;
231525cf1a30Sjl 	uint64_t	size;
231625cf1a30Sjl } mc_mem_range_t;
231725cf1a30Sjl 
231825cf1a30Sjl static int
231925cf1a30Sjl get_base_address(mc_opl_t *mcp)
232025cf1a30Sjl {
232125cf1a30Sjl 	mc_mem_range_t *mem_range;
232225cf1a30Sjl 	int len;
232325cf1a30Sjl 
232425cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
2325d8a0cca9Swh 	    "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) {
232625cf1a30Sjl 		return (DDI_FAILURE);
232725cf1a30Sjl 	}
232825cf1a30Sjl 
232925cf1a30Sjl 	mcp->mc_start_address = mem_range->addr;
233025cf1a30Sjl 	mcp->mc_size = mem_range->size;
233125cf1a30Sjl 
233225cf1a30Sjl 	kmem_free(mem_range, len);
233325cf1a30Sjl 	return (DDI_SUCCESS);
233425cf1a30Sjl }
233525cf1a30Sjl 
233625cf1a30Sjl struct mc_addr_spec {
233725cf1a30Sjl 	uint32_t bank;
233825cf1a30Sjl 	uint32_t phys_hi;
233925cf1a30Sjl 	uint32_t phys_lo;
234025cf1a30Sjl };
234125cf1a30Sjl 
234225cf1a30Sjl #define	REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo)
234325cf1a30Sjl 
234425cf1a30Sjl static char *mc_tbl_name[] = {
234525cf1a30Sjl 	"cs0-mc-pa-trans-table",
234625cf1a30Sjl 	"cs1-mc-pa-trans-table"
234725cf1a30Sjl };
234825cf1a30Sjl 
2349738dd194Shyw /*
2350738dd194Shyw  * This routine performs a rangecheck for a given PA
2351738dd194Shyw  * to see if it belongs to the memory range for this board.
2352738dd194Shyw  * Return 1 if it is valid (within the range) and 0 otherwise
2353738dd194Shyw  */
235425cf1a30Sjl static int
2355738dd194Shyw mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa)
235625cf1a30Sjl {
2357d8a0cca9Swh 	if ((pa < mcp->mc_start_address) || (mcp->mc_start_address +
2358d8a0cca9Swh 	    mcp->mc_size <= pa))
2359738dd194Shyw 		return (0);
2360738dd194Shyw 	else
2361738dd194Shyw 		return (1);
236225cf1a30Sjl }
236325cf1a30Sjl 
236425cf1a30Sjl static void
236525cf1a30Sjl mc_memlist_delete(struct memlist *mlist)
236625cf1a30Sjl {
236725cf1a30Sjl 	struct memlist *ml;
236825cf1a30Sjl 
236925cf1a30Sjl 	for (ml = mlist; ml; ml = mlist) {
237025cf1a30Sjl 		mlist = ml->next;
237125cf1a30Sjl 		kmem_free(ml, sizeof (struct memlist));
237225cf1a30Sjl 	}
237325cf1a30Sjl }
237425cf1a30Sjl 
237525cf1a30Sjl static struct memlist *
237625cf1a30Sjl mc_memlist_dup(struct memlist *mlist)
237725cf1a30Sjl {
237825cf1a30Sjl 	struct memlist *hl = NULL, *tl, **mlp;
237925cf1a30Sjl 
238025cf1a30Sjl 	if (mlist == NULL)
238125cf1a30Sjl 		return (NULL);
238225cf1a30Sjl 
238325cf1a30Sjl 	mlp = &hl;
238425cf1a30Sjl 	tl = *mlp;
238525cf1a30Sjl 	for (; mlist; mlist = mlist->next) {
238625cf1a30Sjl 		*mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP);
238725cf1a30Sjl 		(*mlp)->address = mlist->address;
238825cf1a30Sjl 		(*mlp)->size = mlist->size;
238925cf1a30Sjl 		(*mlp)->prev = tl;
239025cf1a30Sjl 		tl = *mlp;
239125cf1a30Sjl 		mlp = &((*mlp)->next);
239225cf1a30Sjl 	}
239325cf1a30Sjl 	*mlp = NULL;
239425cf1a30Sjl 
239525cf1a30Sjl 	return (hl);
239625cf1a30Sjl }
239725cf1a30Sjl 
239825cf1a30Sjl 
239925cf1a30Sjl static struct memlist *
240025cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len)
240125cf1a30Sjl {
240225cf1a30Sjl 	uint64_t	end;
240325cf1a30Sjl 	struct memlist	*ml, *tl, *nlp;
240425cf1a30Sjl 
240525cf1a30Sjl 	if (mlist == NULL)
240625cf1a30Sjl 		return (NULL);
240725cf1a30Sjl 
240825cf1a30Sjl 	end = base + len;
240925cf1a30Sjl 	if ((end <= mlist->address) || (base == end))
241025cf1a30Sjl 		return (mlist);
241125cf1a30Sjl 
241225cf1a30Sjl 	for (tl = ml = mlist; ml; tl = ml, ml = nlp) {
241325cf1a30Sjl 		uint64_t	mend;
241425cf1a30Sjl 
241525cf1a30Sjl 		nlp = ml->next;
241625cf1a30Sjl 
241725cf1a30Sjl 		if (end <= ml->address)
241825cf1a30Sjl 			break;
241925cf1a30Sjl 
242025cf1a30Sjl 		mend = ml->address + ml->size;
242125cf1a30Sjl 		if (base < mend) {
242225cf1a30Sjl 			if (base <= ml->address) {
242325cf1a30Sjl 				ml->address = end;
242425cf1a30Sjl 				if (end >= mend)
242525cf1a30Sjl 					ml->size = 0ull;
242625cf1a30Sjl 				else
242725cf1a30Sjl 					ml->size = mend - ml->address;
242825cf1a30Sjl 			} else {
242925cf1a30Sjl 				ml->size = base - ml->address;
243025cf1a30Sjl 				if (end < mend) {
243125cf1a30Sjl 					struct memlist	*nl;
243225cf1a30Sjl 					/*
243325cf1a30Sjl 					 * splitting an memlist entry.
243425cf1a30Sjl 					 */
243525cf1a30Sjl 					nl = kmem_alloc(sizeof (struct memlist),
2436d8a0cca9Swh 					    KM_SLEEP);
243725cf1a30Sjl 					nl->address = end;
243825cf1a30Sjl 					nl->size = mend - nl->address;
243925cf1a30Sjl 					if ((nl->next = nlp) != NULL)
244025cf1a30Sjl 						nlp->prev = nl;
244125cf1a30Sjl 					nl->prev = ml;
244225cf1a30Sjl 					ml->next = nl;
244325cf1a30Sjl 					nlp = nl;
244425cf1a30Sjl 				}
244525cf1a30Sjl 			}
244625cf1a30Sjl 			if (ml->size == 0ull) {
244725cf1a30Sjl 				if (ml == mlist) {
244825cf1a30Sjl 					if ((mlist = nlp) != NULL)
244925cf1a30Sjl 						nlp->prev = NULL;
245025cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
245125cf1a30Sjl 					if (mlist == NULL)
245225cf1a30Sjl 						break;
245325cf1a30Sjl 					ml = nlp;
245425cf1a30Sjl 				} else {
245525cf1a30Sjl 					if ((tl->next = nlp) != NULL)
245625cf1a30Sjl 						nlp->prev = tl;
245725cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
245825cf1a30Sjl 					ml = tl;
245925cf1a30Sjl 				}
246025cf1a30Sjl 			}
246125cf1a30Sjl 		}
246225cf1a30Sjl 	}
246325cf1a30Sjl 
246425cf1a30Sjl 	return (mlist);
246525cf1a30Sjl }
246625cf1a30Sjl 
246725cf1a30Sjl static void
246825cf1a30Sjl mc_get_mlist(mc_opl_t *mcp)
246925cf1a30Sjl {
247025cf1a30Sjl 	struct memlist *mlist;
247125cf1a30Sjl 
247225cf1a30Sjl 	memlist_read_lock();
247325cf1a30Sjl 	mlist = mc_memlist_dup(phys_install);
247425cf1a30Sjl 	memlist_read_unlock();
247525cf1a30Sjl 
247625cf1a30Sjl 	if (mlist) {
247725cf1a30Sjl 		mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address);
247825cf1a30Sjl 	}
247925cf1a30Sjl 
248025cf1a30Sjl 	if (mlist) {
248125cf1a30Sjl 		uint64_t startpa, endpa;
248225cf1a30Sjl 
248325cf1a30Sjl 		startpa = mcp->mc_start_address + mcp->mc_size;
248425cf1a30Sjl 		endpa = ptob(physmax + 1);
248525cf1a30Sjl 		if (endpa > startpa) {
2486d8a0cca9Swh 			mlist = mc_memlist_del_span(mlist, startpa,
2487d8a0cca9Swh 			    endpa - startpa);
248825cf1a30Sjl 		}
248925cf1a30Sjl 	}
249025cf1a30Sjl 
249125cf1a30Sjl 	if (mlist) {
249225cf1a30Sjl 		mcp->mlist = mlist;
249325cf1a30Sjl 	}
249425cf1a30Sjl }
249525cf1a30Sjl 
249625cf1a30Sjl int
249725cf1a30Sjl mc_board_add(mc_opl_t *mcp)
249825cf1a30Sjl {
249925cf1a30Sjl 	struct mc_addr_spec *macaddr;
25000cc8ae86Sav 	cs_status_t *cs_status;
25010cc8ae86Sav 	int len, len1, i, bk, cc;
2502738dd194Shyw 	mc_rsaddr_info_t rsaddr;
250325cf1a30Sjl 	uint32_t mirr;
25040cc8ae86Sav 	int nbanks = 0;
25050cc8ae86Sav 	uint64_t nbytes = 0;
2506d8a0cca9Swh 	int mirror_mode = 0;
2507d8a0cca9Swh 	int ret;
250825cf1a30Sjl 
250925cf1a30Sjl 	/*
251025cf1a30Sjl 	 * Get configurations from "pseudo-mc" node which includes:
251125cf1a30Sjl 	 * board# : LSB number
251225cf1a30Sjl 	 * mac-addr : physical base address of MAC registers
251325cf1a30Sjl 	 * csX-mac-pa-trans-table: translation table from DIMM address
251425cf1a30Sjl 	 *			to physical address or vice versa.
251525cf1a30Sjl 	 */
251625cf1a30Sjl 	mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip,
2517d8a0cca9Swh 	    DDI_PROP_DONTPASS, "board#", -1);
251825cf1a30Sjl 
25190cc8ae86Sav 	if (mcp->mc_board_num == -1) {
25200cc8ae86Sav 		return (DDI_FAILURE);
25210cc8ae86Sav 	}
25220cc8ae86Sav 
252325cf1a30Sjl 	/*
252425cf1a30Sjl 	 * Get start address in this CAB. It can be gotten from
252525cf1a30Sjl 	 * "sb-mem-ranges" property.
252625cf1a30Sjl 	 */
252725cf1a30Sjl 
252825cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
252925cf1a30Sjl 		return (DDI_FAILURE);
253025cf1a30Sjl 	}
253125cf1a30Sjl 	/* get mac-pa trans tables */
253225cf1a30Sjl 	for (i = 0; i < MC_TT_CS; i++) {
253325cf1a30Sjl 		len = MC_TT_ENTRIES;
253425cf1a30Sjl 		cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip,
2535d8a0cca9Swh 		    DDI_PROP_DONTPASS, mc_tbl_name[i],
2536d8a0cca9Swh 		    (caddr_t)mcp->mc_trans_table[i], &len);
253725cf1a30Sjl 
253825cf1a30Sjl 		if (cc != DDI_SUCCESS) {
253925cf1a30Sjl 			bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES);
254025cf1a30Sjl 		}
254125cf1a30Sjl 	}
254225cf1a30Sjl 	mcp->mlist = NULL;
254325cf1a30Sjl 
254425cf1a30Sjl 	mc_get_mlist(mcp);
254525cf1a30Sjl 
254625cf1a30Sjl 	/* initialize bank informations */
254725cf1a30Sjl 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
2548d8a0cca9Swh 	    "mc-addr", (caddr_t)&macaddr, &len);
254925cf1a30Sjl 	if (cc != DDI_SUCCESS) {
255025cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc);
255125cf1a30Sjl 		return (DDI_FAILURE);
255225cf1a30Sjl 	}
255325cf1a30Sjl 
25540cc8ae86Sav 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
2555d8a0cca9Swh 	    "cs-status", (caddr_t)&cs_status, &len1);
255625cf1a30Sjl 
25570cc8ae86Sav 	if (cc != DDI_SUCCESS) {
25580cc8ae86Sav 		if (len > 0)
25590cc8ae86Sav 			kmem_free(macaddr, len);
25600cc8ae86Sav 		cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc);
25610cc8ae86Sav 		return (DDI_FAILURE);
25620cc8ae86Sav 	}
2563aeb241b2Sav 	/* get the physical board number for a given logical board number */
2564aeb241b2Sav 	mcp->mc_phys_board_num = mc_opl_get_physical_board(mcp->mc_board_num);
2565aeb241b2Sav 
2566aeb241b2Sav 	if (mcp->mc_phys_board_num < 0) {
2567aeb241b2Sav 		if (len > 0)
2568aeb241b2Sav 			kmem_free(macaddr, len);
2569aeb241b2Sav 		cmn_err(CE_WARN, "Unable to obtain the physical board number");
2570aeb241b2Sav 		return (DDI_FAILURE);
2571aeb241b2Sav 	}
257225cf1a30Sjl 
25730cc8ae86Sav 	mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL);
25740cc8ae86Sav 
25750cc8ae86Sav 	for (i = 0; i < len1 / sizeof (cs_status_t); i++) {
25760cc8ae86Sav 		nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) |
2577d8a0cca9Swh 		    ((uint64_t)cs_status[i].cs_avail_low);
25780cc8ae86Sav 	}
25790cc8ae86Sav 	if (len1 > 0)
25800cc8ae86Sav 		kmem_free(cs_status, len1);
25810cc8ae86Sav 	nbanks = len / sizeof (struct mc_addr_spec);
25820cc8ae86Sav 
25830cc8ae86Sav 	if (nbanks > 0)
25840cc8ae86Sav 		nbytes /= nbanks;
25850cc8ae86Sav 	else {
25860cc8ae86Sav 		/* No need to free macaddr because len must be 0 */
25870cc8ae86Sav 		mcp->mc_status |= MC_MEMORYLESS;
25880cc8ae86Sav 		return (DDI_SUCCESS);
25890cc8ae86Sav 	}
25900cc8ae86Sav 
25910cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
25920cc8ae86Sav 		mcp->mc_scf_retry[i] = 0;
25930cc8ae86Sav 		mcp->mc_period[i] = 0;
25940cc8ae86Sav 		mcp->mc_speedup_period[i] = 0;
25950cc8ae86Sav 	}
25960cc8ae86Sav 
25970cc8ae86Sav 	/*
25980cc8ae86Sav 	 * Get the memory size here. Let it be B (bytes).
25990cc8ae86Sav 	 * Let T be the time in u.s. to scan 64 bytes.
26000cc8ae86Sav 	 * If we want to complete 1 round of scanning in P seconds.
26010cc8ae86Sav 	 *
26020cc8ae86Sav 	 *	B * T * 10^(-6)	= P
26030cc8ae86Sav 	 *	---------------
26040cc8ae86Sav 	 *		64
26050cc8ae86Sav 	 *
26060cc8ae86Sav 	 *	T = P * 64 * 10^6
26070cc8ae86Sav 	 *	    -------------
26080cc8ae86Sav 	 *		B
26090cc8ae86Sav 	 *
26100cc8ae86Sav 	 *	  = P * 64 * 10^6
26110cc8ae86Sav 	 *	    -------------
26120cc8ae86Sav 	 *		B
26130cc8ae86Sav 	 *
26140cc8ae86Sav 	 *	The timing bits are set in PTRL_CNTL[28:26] where
26150cc8ae86Sav 	 *
26160cc8ae86Sav 	 *	0	- 1 m.s
26170cc8ae86Sav 	 *	1	- 512 u.s.
26180cc8ae86Sav 	 *	10	- 256 u.s.
26190cc8ae86Sav 	 *	11	- 128 u.s.
26200cc8ae86Sav 	 *	100	- 64 u.s.
26210cc8ae86Sav 	 *	101	- 32 u.s.
26220cc8ae86Sav 	 *	110	- 0 u.s.
26230cc8ae86Sav 	 *	111	- reserved.
26240cc8ae86Sav 	 *
26250cc8ae86Sav 	 *
26260cc8ae86Sav 	 *	a[0] = 110, a[1] = 101, ... a[6] = 0
26270cc8ae86Sav 	 *
26280cc8ae86Sav 	 *	cs-status property is int x 7
26290cc8ae86Sav 	 *	0 - cs#
26300cc8ae86Sav 	 *	1 - cs-status
26310cc8ae86Sav 	 *	2 - cs-avail.hi
26320cc8ae86Sav 	 *	3 - cs-avail.lo
26330cc8ae86Sav 	 *	4 - dimm-capa.hi
26340cc8ae86Sav 	 *	5 - dimm-capa.lo
26350cc8ae86Sav 	 *	6 - #of dimms
26360cc8ae86Sav 	 */
26370cc8ae86Sav 
26380cc8ae86Sav 	if (nbytes > 0) {
26390cc8ae86Sav 		int i;
26400cc8ae86Sav 		uint64_t ms;
26410cc8ae86Sav 		ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes;
26420cc8ae86Sav 		mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds;
26430cc8ae86Sav 		for (i = 0; i < MC_MAX_SPEEDS - 1; i++) {
26440cc8ae86Sav 			if (ms < mc_scan_speeds[i + 1].mc_period) {
26450cc8ae86Sav 				mcp->mc_speed = mc_scan_speeds[i].mc_speeds;
26460cc8ae86Sav 				break;
26470cc8ae86Sav 			}
26480cc8ae86Sav 		}
26490cc8ae86Sav 	} else
26500cc8ae86Sav 		mcp->mc_speed = 0;
26510cc8ae86Sav 
26520cc8ae86Sav 
26530cc8ae86Sav 	for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) {
26540cc8ae86Sav 		struct mc_bank *bankp;
2655*601c2e1eSdhain 		mc_retry_info_t *retry;
26560cc8ae86Sav 		uint32_t reg;
2657*601c2e1eSdhain 		int k;
26580cc8ae86Sav 
26590cc8ae86Sav 		/*
26600cc8ae86Sav 		 * setup bank
26610cc8ae86Sav 		 */
26620cc8ae86Sav 		bk = macaddr[i].bank;
26630cc8ae86Sav 		bankp = &(mcp->mc_bank[bk]);
26640cc8ae86Sav 		bankp->mcb_status = BANK_INSTALLED;
26650cc8ae86Sav 		bankp->mcb_reg_base = REGS_PA(macaddr, i);
26660cc8ae86Sav 
2667*601c2e1eSdhain 		bankp->mcb_retry_freelist = NULL;
2668*601c2e1eSdhain 		bankp->mcb_retry_pending = NULL;
2669*601c2e1eSdhain 		bankp->mcb_active = NULL;
2670*601c2e1eSdhain 		retry = &bankp->mcb_retry_infos[0];
2671*601c2e1eSdhain 		for (k = 0; k < MC_RETRY_COUNT; k++, retry++) {
2672*601c2e1eSdhain 			mc_retry_info_put(&bankp->mcb_retry_freelist, retry);
2673*601c2e1eSdhain 		}
2674*601c2e1eSdhain 
26750cc8ae86Sav 		reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk));
26760cc8ae86Sav 		bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS);
267725cf1a30Sjl 
267825cf1a30Sjl 		/*
267925cf1a30Sjl 		 * check if mirror mode
268025cf1a30Sjl 		 */
268125cf1a30Sjl 		mirr = LD_MAC_REG(MAC_MIRR(mcp, bk));
268225cf1a30Sjl 
268325cf1a30Sjl 		if (mirr & MAC_MIRR_MIRROR_MODE) {
2684d8a0cca9Swh 			MC_LOG("Mirror -> /LSB%d/B%d\n", mcp->mc_board_num,
2685d8a0cca9Swh 			    bk);
268625cf1a30Sjl 			bankp->mcb_status |= BANK_MIRROR_MODE;
2687d8a0cca9Swh 			mirror_mode = 1;
268825cf1a30Sjl 			/*
268925cf1a30Sjl 			 * The following bit is only used for
269025cf1a30Sjl 			 * error injection.  We should clear it
269125cf1a30Sjl 			 */
269225cf1a30Sjl 			if (mirr & MAC_MIRR_BANK_EXCLUSIVE)
2693d8a0cca9Swh 				ST_MAC_REG(MAC_MIRR(mcp, bk), 0);
269425cf1a30Sjl 		}
269525cf1a30Sjl 
269625cf1a30Sjl 		/*
269725cf1a30Sjl 		 * restart if not mirror mode or the other bank
269825cf1a30Sjl 		 * of the mirror is not running
269925cf1a30Sjl 		 */
270025cf1a30Sjl 		if (!(mirr & MAC_MIRR_MIRROR_MODE) ||
2701d8a0cca9Swh 		    !(mcp->mc_bank[bk^1].mcb_status & BANK_PTRL_RUNNING)) {
2702d8a0cca9Swh 			MC_LOG("Starting up /LSB%d/B%d\n", mcp->mc_board_num,
2703d8a0cca9Swh 			    bk);
2704738dd194Shyw 			get_ptrl_start_address(mcp, bk, &rsaddr.mi_restartaddr);
2705738dd194Shyw 			rsaddr.mi_valid = 0;
2706738dd194Shyw 			rsaddr.mi_injectrestart = 0;
2707738dd194Shyw 			restart_patrol(mcp, bk, &rsaddr);
270825cf1a30Sjl 		} else {
270925cf1a30Sjl 			MC_LOG("Not starting up /LSB%d/B%d\n",
2710d8a0cca9Swh 			    mcp->mc_board_num, bk);
271125cf1a30Sjl 		}
271225cf1a30Sjl 		bankp->mcb_status |= BANK_PTRL_RUNNING;
271325cf1a30Sjl 	}
27140cc8ae86Sav 	if (len > 0)
27150cc8ae86Sav 		kmem_free(macaddr, len);
27160cc8ae86Sav 
2717d8a0cca9Swh 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, mcp->mc_dip, "mirror-mode",
2718d8a0cca9Swh 	    mirror_mode);
2719d8a0cca9Swh 	if (ret != DDI_PROP_SUCCESS) {
2720d8a0cca9Swh 		cmn_err(CE_WARN, "Unable to update mirror-mode property");
2721d8a0cca9Swh 	}
2722d8a0cca9Swh 
27230cc8ae86Sav 	mcp->mc_dimm_list = mc_get_dimm_list(mcp);
272425cf1a30Sjl 
272525cf1a30Sjl 	/*
272625cf1a30Sjl 	 * set interval in HZ.
272725cf1a30Sjl 	 */
272825cf1a30Sjl 	mcp->mc_last_error = 0;
272925cf1a30Sjl 
273025cf1a30Sjl 	/* restart memory patrol checking */
273125cf1a30Sjl 	mcp->mc_status |= MC_POLL_RUNNING;
273225cf1a30Sjl 
273325cf1a30Sjl 	return (DDI_SUCCESS);
273425cf1a30Sjl }
273525cf1a30Sjl 
273625cf1a30Sjl int
273725cf1a30Sjl mc_board_del(mc_opl_t *mcp)
273825cf1a30Sjl {
273925cf1a30Sjl 	int i;
274025cf1a30Sjl 	scf_log_t *p;
274125cf1a30Sjl 
274225cf1a30Sjl 	/*
274325cf1a30Sjl 	 * cleanup mac state
274425cf1a30Sjl 	 */
274525cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
27460cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
27470cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
27480cc8ae86Sav 		mutex_destroy(&mcp->mc_lock);
27490cc8ae86Sav 		return (DDI_SUCCESS);
27500cc8ae86Sav 	}
275125cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
275225cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
275325cf1a30Sjl 			mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED;
275425cf1a30Sjl 		}
275525cf1a30Sjl 	}
275625cf1a30Sjl 
275725cf1a30Sjl 	/* stop memory patrol checking */
2758738dd194Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
275925cf1a30Sjl 
276025cf1a30Sjl 	/* just throw away all the scf logs */
27610cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
2762d8a0cca9Swh 		while ((p = mcp->mc_scf_log[i]) != NULL) {
2763d8a0cca9Swh 			mcp->mc_scf_log[i] = p->sl_next;
2764d8a0cca9Swh 			mcp->mc_scf_total[i]--;
2765d8a0cca9Swh 			kmem_free(p, sizeof (scf_log_t));
2766d8a0cca9Swh 		}
276725cf1a30Sjl 	}
276825cf1a30Sjl 
276925cf1a30Sjl 	if (mcp->mlist)
277025cf1a30Sjl 		mc_memlist_delete(mcp->mlist);
277125cf1a30Sjl 
27720cc8ae86Sav 	if (mcp->mc_dimm_list)
27730cc8ae86Sav 		mc_free_dimm_list(mcp->mc_dimm_list);
27740cc8ae86Sav 
277525cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
277625cf1a30Sjl 
277725cf1a30Sjl 	mutex_destroy(&mcp->mc_lock);
277825cf1a30Sjl 	return (DDI_SUCCESS);
277925cf1a30Sjl }
278025cf1a30Sjl 
278125cf1a30Sjl int
278225cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag)
278325cf1a30Sjl {
278425cf1a30Sjl 	/* stop memory patrol checking */
278525cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
27860cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
27870cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
27880cc8ae86Sav 		return (DDI_SUCCESS);
27890cc8ae86Sav 	}
27900cc8ae86Sav 
2791738dd194Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
2792738dd194Shyw 
279325cf1a30Sjl 	mcp->mc_status |= flag;
279425cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
279525cf1a30Sjl 
279625cf1a30Sjl 	return (DDI_SUCCESS);
279725cf1a30Sjl }
279825cf1a30Sjl 
279968ac2337Sjl void
280068ac2337Sjl opl_mc_update_mlist(void)
280168ac2337Sjl {
280268ac2337Sjl 	int i;
280368ac2337Sjl 	mc_opl_t *mcp;
280468ac2337Sjl 
280568ac2337Sjl 	/*
280668ac2337Sjl 	 * memory information is not updated until
280768ac2337Sjl 	 * the post attach/detach stage during DR.
280868ac2337Sjl 	 * This interface is used by dr_mem to inform
280968ac2337Sjl 	 * mc-opl to update the mlist.
281068ac2337Sjl 	 */
281168ac2337Sjl 
281268ac2337Sjl 	mutex_enter(&mcmutex);
281368ac2337Sjl 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
281468ac2337Sjl 		if ((mcp = mc_instances[i]) == NULL)
281568ac2337Sjl 			continue;
281668ac2337Sjl 		mutex_enter(&mcp->mc_lock);
281768ac2337Sjl 		if (mcp->mlist)
281868ac2337Sjl 			mc_memlist_delete(mcp->mlist);
281968ac2337Sjl 		mcp->mlist = NULL;
282068ac2337Sjl 		mc_get_mlist(mcp);
282168ac2337Sjl 		mutex_exit(&mcp->mc_lock);
282268ac2337Sjl 	}
282368ac2337Sjl 	mutex_exit(&mcmutex);
282468ac2337Sjl }
282568ac2337Sjl 
282625cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */
282725cf1a30Sjl 
282825cf1a30Sjl int
282925cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag)
283025cf1a30Sjl {
283125cf1a30Sjl 	int i;
283225cf1a30Sjl 	uint64_t basepa;
283325cf1a30Sjl 
283425cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
28350cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
28360cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
28370cc8ae86Sav 		return (DDI_SUCCESS);
28380cc8ae86Sav 	}
283925cf1a30Sjl 	basepa = mcp->mc_start_address;
284025cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
284125cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
284225cf1a30Sjl 		return (DDI_FAILURE);
284325cf1a30Sjl 	}
284425cf1a30Sjl 
284525cf1a30Sjl 	if (basepa != mcp->mc_start_address) {
284625cf1a30Sjl 		if (mcp->mlist)
284725cf1a30Sjl 			mc_memlist_delete(mcp->mlist);
284825cf1a30Sjl 		mcp->mlist = NULL;
284925cf1a30Sjl 		mc_get_mlist(mcp);
285025cf1a30Sjl 	}
285125cf1a30Sjl 
285225cf1a30Sjl 	mcp->mc_status &= ~flag;
285325cf1a30Sjl 
285425cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
285525cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
285625cf1a30Sjl 		return (DDI_SUCCESS);
285725cf1a30Sjl 	}
285825cf1a30Sjl 
285925cf1a30Sjl 	if (!(mcp->mc_status & MC_POLL_RUNNING)) {
286025cf1a30Sjl 		/* restart memory patrol checking */
286125cf1a30Sjl 		mcp->mc_status |= MC_POLL_RUNNING;
286225cf1a30Sjl 		for (i = 0; i < BANKNUM_PER_SB; i++) {
286325cf1a30Sjl 			if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
2864*601c2e1eSdhain 				mc_check_errors_func(mcp);
286525cf1a30Sjl 			}
286625cf1a30Sjl 		}
286725cf1a30Sjl 	}
286825cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
286925cf1a30Sjl 
287025cf1a30Sjl 	return (DDI_SUCCESS);
287125cf1a30Sjl }
287225cf1a30Sjl 
287325cf1a30Sjl static mc_opl_t *
287425cf1a30Sjl mc_pa_to_mcp(uint64_t pa)
287525cf1a30Sjl {
28760cc8ae86Sav 	mc_opl_t *mcp;
28770cc8ae86Sav 	int i;
28780cc8ae86Sav 
287925cf1a30Sjl 	ASSERT(MUTEX_HELD(&mcmutex));
28800cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
28810cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
28820cc8ae86Sav 			continue;
288325cf1a30Sjl 		/* if mac patrol is suspended, we cannot rely on it */
28840cc8ae86Sav 		if (!(mcp->mc_status & MC_POLL_RUNNING) ||
2885d8a0cca9Swh 		    (mcp->mc_status & MC_SOFT_SUSPENDED))
288625cf1a30Sjl 			continue;
2887738dd194Shyw 		if (mc_rangecheck_pa(mcp, pa)) {
28880cc8ae86Sav 			return (mcp);
288925cf1a30Sjl 		}
289025cf1a30Sjl 	}
289125cf1a30Sjl 	return (NULL);
289225cf1a30Sjl }
289325cf1a30Sjl 
289425cf1a30Sjl /*
289525cf1a30Sjl  * Get Physical Board number from Logical one.
289625cf1a30Sjl  */
289725cf1a30Sjl static int
289825cf1a30Sjl mc_opl_get_physical_board(int sb)
289925cf1a30Sjl {
290025cf1a30Sjl 	if (&opl_get_physical_board) {
290125cf1a30Sjl 		return (opl_get_physical_board(sb));
290225cf1a30Sjl 	}
290325cf1a30Sjl 
290425cf1a30Sjl 	cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n");
290525cf1a30Sjl 	return (-1);
290625cf1a30Sjl }
290725cf1a30Sjl 
290825cf1a30Sjl /* ARGSUSED */
290925cf1a30Sjl int
291025cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen,
291125cf1a30Sjl 	int *lenp)
291225cf1a30Sjl {
29130cc8ae86Sav 	int i;
2914aeb241b2Sav 	int j;
291525cf1a30Sjl 	int sb;
29160cc8ae86Sav 	int bank;
2917aeb241b2Sav 	int cs;
29180cc8ae86Sav 	mc_opl_t *mcp;
29190cc8ae86Sav 	char memb_num;
292025cf1a30Sjl 
292125cf1a30Sjl 	mutex_enter(&mcmutex);
292225cf1a30Sjl 
292325cf1a30Sjl 	if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) ||
2924d8a0cca9Swh 	    (!pa_is_valid(mcp, flt_addr))) {
292525cf1a30Sjl 		mutex_exit(&mcmutex);
292625cf1a30Sjl 		if (snprintf(buf, buflen, "UNKNOWN") >= buflen) {
292725cf1a30Sjl 			return (ENOSPC);
292825cf1a30Sjl 		} else {
292925cf1a30Sjl 			if (lenp)
293025cf1a30Sjl 				*lenp = strlen(buf);
293125cf1a30Sjl 		}
293225cf1a30Sjl 		return (0);
293325cf1a30Sjl 	}
293425cf1a30Sjl 
293525cf1a30Sjl 	bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address);
2936aeb241b2Sav 	sb = mcp->mc_phys_board_num;
2937aeb241b2Sav 	cs = pa_to_cs(mcp, flt_addr - mcp->mc_start_address);
293825cf1a30Sjl 
293925cf1a30Sjl 	if (sb == -1) {
294025cf1a30Sjl 		mutex_exit(&mcmutex);
294125cf1a30Sjl 		return (ENXIO);
294225cf1a30Sjl 	}
294325cf1a30Sjl 
29440cc8ae86Sav 	if (plat_model == MODEL_DC) {
29450cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
2946aeb241b2Sav 		j = (cs == 0) ? i : i + 2;
2947aeb241b2Sav 		snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
29480cc8ae86Sav 		    model_names[plat_model].unit_name, sb,
2949aeb241b2Sav 		    mc_dc_dimm_unum_table[j],
2950aeb241b2Sav 		    mc_dc_dimm_unum_table[j + 1]);
295125cf1a30Sjl 	} else {
29520cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
2953aeb241b2Sav 		j = (cs == 0) ? i : i + 2;
29540cc8ae86Sav 		memb_num = mc_ff_dimm_unum_table[i][0];
2955aeb241b2Sav 		snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s",
29560cc8ae86Sav 		    model_names[plat_model].unit_name,
29570cc8ae86Sav 		    model_names[plat_model].mem_name, memb_num,
2958aeb241b2Sav 		    &mc_ff_dimm_unum_table[j][1],
2959aeb241b2Sav 		    &mc_ff_dimm_unum_table[j + 1][1]);
29600cc8ae86Sav 	}
29610cc8ae86Sav 	if (lenp) {
29620cc8ae86Sav 		*lenp = strlen(buf);
296325cf1a30Sjl 	}
296425cf1a30Sjl 	mutex_exit(&mcmutex);
296525cf1a30Sjl 	return (0);
296625cf1a30Sjl }
296725cf1a30Sjl 
296825cf1a30Sjl int
29690cc8ae86Sav opl_mc_suspend(void)
297025cf1a30Sjl {
297125cf1a30Sjl 	mc_opl_t *mcp;
29720cc8ae86Sav 	int i;
297325cf1a30Sjl 
297425cf1a30Sjl 	mutex_enter(&mcmutex);
29750cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
29760cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
29770cc8ae86Sav 			continue;
29780cc8ae86Sav 		mc_suspend(mcp, MC_SOFT_SUSPENDED);
297925cf1a30Sjl 	}
298025cf1a30Sjl 	mutex_exit(&mcmutex);
29810cc8ae86Sav 
298225cf1a30Sjl 	return (0);
298325cf1a30Sjl }
298425cf1a30Sjl 
298525cf1a30Sjl int
29860cc8ae86Sav opl_mc_resume(void)
298725cf1a30Sjl {
298825cf1a30Sjl 	mc_opl_t *mcp;
29890cc8ae86Sav 	int i;
299025cf1a30Sjl 
299125cf1a30Sjl 	mutex_enter(&mcmutex);
29920cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
29930cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
29940cc8ae86Sav 			continue;
29950cc8ae86Sav 		mc_resume(mcp, MC_SOFT_SUSPENDED);
299625cf1a30Sjl 	}
299725cf1a30Sjl 	mutex_exit(&mcmutex);
29980cc8ae86Sav 
299925cf1a30Sjl 	return (0);
300025cf1a30Sjl }
300125cf1a30Sjl static void
300225cf1a30Sjl insert_mcp(mc_opl_t *mcp)
300325cf1a30Sjl {
300425cf1a30Sjl 	mutex_enter(&mcmutex);
30050cc8ae86Sav 	if (mc_instances[mcp->mc_board_num] != NULL) {
30060cc8ae86Sav 		MC_LOG("mc-opl instance for board# %d already exists\n",
3007d8a0cca9Swh 		    mcp->mc_board_num);
30080cc8ae86Sav 	}
30090cc8ae86Sav 	mc_instances[mcp->mc_board_num] = mcp;
301025cf1a30Sjl 	mutex_exit(&mcmutex);
301125cf1a30Sjl }
301225cf1a30Sjl 
301325cf1a30Sjl static void
301425cf1a30Sjl delete_mcp(mc_opl_t *mcp)
301525cf1a30Sjl {
30160cc8ae86Sav 	mutex_enter(&mcmutex);
30170cc8ae86Sav 	mc_instances[mcp->mc_board_num] = 0;
30180cc8ae86Sav 	mutex_exit(&mcmutex);
301925cf1a30Sjl }
302025cf1a30Sjl 
302125cf1a30Sjl /* Error injection interface */
302225cf1a30Sjl 
3023cfb9e062Shyw static void
3024cfb9e062Shyw mc_lock_va(uint64_t pa, caddr_t new_va)
3025cfb9e062Shyw {
3026cfb9e062Shyw 	tte_t tte;
3027cfb9e062Shyw 
3028738dd194Shyw 	vtag_flushpage(new_va, (uint64_t)ksfmmup);
3029d8a0cca9Swh 	sfmmu_memtte(&tte, pa >> PAGESHIFT, PROC_DATA|HAT_NOSYNC, TTE8K);
3030cfb9e062Shyw 	tte.tte_intlo |= TTE_LCK_INT;
3031cfb9e062Shyw 	sfmmu_dtlb_ld_kva(new_va, &tte);
3032cfb9e062Shyw }
3033cfb9e062Shyw 
3034cfb9e062Shyw static void
3035cfb9e062Shyw mc_unlock_va(caddr_t va)
3036cfb9e062Shyw {
3037cfb9e062Shyw 	vtag_flushpage(va, (uint64_t)ksfmmup);
3038cfb9e062Shyw }
3039cfb9e062Shyw 
304025cf1a30Sjl /* ARGSUSED */
304125cf1a30Sjl int
304225cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags)
304325cf1a30Sjl {
304425cf1a30Sjl 	mc_opl_t *mcp;
304525cf1a30Sjl 	int bank;
304625cf1a30Sjl 	uint32_t dimm_addr;
304725cf1a30Sjl 	uint32_t cntl;
3048738dd194Shyw 	mc_rsaddr_info_t rsaddr;
304925cf1a30Sjl 	uint32_t data, stat;
305025cf1a30Sjl 	int both_sides = 0;
305125cf1a30Sjl 	uint64_t pa0;
3052cfb9e062Shyw 	int extra_injection_needed = 0;
305325cf1a30Sjl 	extern void cpu_flush_ecache(void);
305425cf1a30Sjl 
305525cf1a30Sjl 	MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags);
305625cf1a30Sjl 
305725cf1a30Sjl 	mutex_enter(&mcmutex);
305825cf1a30Sjl 	if ((mcp = mc_pa_to_mcp(pa)) == NULL) {
305925cf1a30Sjl 		mutex_exit(&mcmutex);
306025cf1a30Sjl 		MC_LOG("mc_inject_error: invalid pa\n");
306125cf1a30Sjl 		return (ENOTSUP);
306225cf1a30Sjl 	}
306325cf1a30Sjl 
306425cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
306525cf1a30Sjl 	mutex_exit(&mcmutex);
306625cf1a30Sjl 
306725cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
306825cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
306925cf1a30Sjl 		MC_LOG("mc-opl has been suspended.  No error injection.\n");
307025cf1a30Sjl 		return (EBUSY);
307125cf1a30Sjl 	}
307225cf1a30Sjl 
307325cf1a30Sjl 	/* convert pa to offset within the board */
307425cf1a30Sjl 	MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address);
307525cf1a30Sjl 
307625cf1a30Sjl 	if (!pa_is_valid(mcp, pa)) {
307725cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
307825cf1a30Sjl 		return (EINVAL);
307925cf1a30Sjl 	}
308025cf1a30Sjl 
308125cf1a30Sjl 	pa0 = pa - mcp->mc_start_address;
308225cf1a30Sjl 
308325cf1a30Sjl 	bank = pa_to_bank(mcp, pa0);
308425cf1a30Sjl 
308525cf1a30Sjl 	if (flags & MC_INJECT_FLAG_OTHER)
308625cf1a30Sjl 		bank = bank ^ 1;
308725cf1a30Sjl 
308825cf1a30Sjl 	if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) {
308925cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
309025cf1a30Sjl 		MC_LOG("Not mirror mode\n");
309125cf1a30Sjl 		return (EINVAL);
309225cf1a30Sjl 	}
309325cf1a30Sjl 
309425cf1a30Sjl 	dimm_addr = pa_to_dimm(mcp, pa0);
309525cf1a30Sjl 
3096d8a0cca9Swh 	MC_LOG("injecting error to /LSB%d/B%d/%x\n", mcp->mc_board_num, bank,
3097d8a0cca9Swh 	    dimm_addr);
309825cf1a30Sjl 
309925cf1a30Sjl 
310025cf1a30Sjl 	switch (error_type) {
310125cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
310225cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
310325cf1a30Sjl 	case MC_INJECT_MUE:
310425cf1a30Sjl 		both_sides = 1;
310525cf1a30Sjl 	}
310625cf1a30Sjl 
310725cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESET)
310825cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0);
310925cf1a30Sjl 
311025cf1a30Sjl 	ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK);
311125cf1a30Sjl 
311225cf1a30Sjl 	if (both_sides) {
311325cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0);
3114d8a0cca9Swh 		ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), dimm_addr &
3115d8a0cca9Swh 		    MAC_EG_ADD_MASK);
311625cf1a30Sjl 	}
311725cf1a30Sjl 
311825cf1a30Sjl 	switch (error_type) {
311925cf1a30Sjl 	case MC_INJECT_SUE:
3120cfb9e062Shyw 		extra_injection_needed = 1;
3121cfb9e062Shyw 		/*FALLTHROUGH*/
3122cfb9e062Shyw 	case MC_INJECT_UE:
312325cf1a30Sjl 	case MC_INJECT_MUE:
312425cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
3125d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 |
3126d8a0cca9Swh 			    MAC_EG_FORCE_READ16 | MAC_EG_RDERR_ONCE;
312725cf1a30Sjl 		} else {
3128d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR00 |
3129d8a0cca9Swh 			    MAC_EG_FORCE_DERR16 | MAC_EG_DERR_ONCE;
313025cf1a30Sjl 		}
313125cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
313225cf1a30Sjl 		break;
313325cf1a30Sjl 	case MC_INJECT_INTERMITTENT_CE:
313425cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
313525cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
3136d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX |MAC_EG_FORCE_READ00 |
3137d8a0cca9Swh 			    MAC_EG_RDERR_ONCE;
313825cf1a30Sjl 		} else {
3139d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 |
3140d8a0cca9Swh 			    MAC_EG_DERR_ONCE;
314125cf1a30Sjl 		}
3142cfb9e062Shyw 		extra_injection_needed = 1;
314325cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
314425cf1a30Sjl 		break;
314525cf1a30Sjl 	case MC_INJECT_PERMANENT_CE:
314625cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
314725cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
3148d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 |
3149d8a0cca9Swh 			    MAC_EG_RDERR_ALWAYS;
315025cf1a30Sjl 		} else {
3151d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 |
3152d8a0cca9Swh 			    MAC_EG_DERR_ALWAYS;
315325cf1a30Sjl 		}
315425cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
315525cf1a30Sjl 		break;
315625cf1a30Sjl 	case MC_INJECT_CMPE:
315725cf1a30Sjl 		data = 0xabcdefab;
315825cf1a30Sjl 		stphys(pa, data);
315925cf1a30Sjl 		cpu_flush_ecache();
316025cf1a30Sjl 		MC_LOG("CMPE: writing data %x to %lx\n", data, pa);
316125cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE);
316225cf1a30Sjl 		stphys(pa, data ^ 0xffffffff);
3163738dd194Shyw 		membar_sync();
316425cf1a30Sjl 		cpu_flush_ecache();
316525cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), 0);
316625cf1a30Sjl 		MC_LOG("CMPE: write new data %xto %lx\n", data, pa);
316725cf1a30Sjl 		cntl = 0;
316825cf1a30Sjl 		break;
316925cf1a30Sjl 	case MC_INJECT_NOP:
317025cf1a30Sjl 		cntl = 0;
317125cf1a30Sjl 		break;
317225cf1a30Sjl 	default:
317325cf1a30Sjl 		MC_LOG("mc_inject_error: invalid option\n");
317425cf1a30Sjl 		cntl = 0;
317525cf1a30Sjl 	}
317625cf1a30Sjl 
317725cf1a30Sjl 	if (cntl) {
317825cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK);
317925cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
318025cf1a30Sjl 
318125cf1a30Sjl 		if (both_sides) {
318225cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
3183d8a0cca9Swh 			    MAC_EG_SETUP_MASK);
318425cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
318525cf1a30Sjl 		}
318625cf1a30Sjl 	}
318725cf1a30Sjl 
318825cf1a30Sjl 	/*
318925cf1a30Sjl 	 * For all injection cases except compare error, we
319025cf1a30Sjl 	 * must write to the PA to trigger the error.
319125cf1a30Sjl 	 */
319225cf1a30Sjl 
319325cf1a30Sjl 	if (flags & MC_INJECT_FLAG_ST) {
319425cf1a30Sjl 		data = 0xf0e0d0c0;
319525cf1a30Sjl 		MC_LOG("Writing %x to %lx\n", data, pa);
319625cf1a30Sjl 		stphys(pa, data);
319725cf1a30Sjl 		cpu_flush_ecache();
319825cf1a30Sjl 	}
319925cf1a30Sjl 
320025cf1a30Sjl 
320125cf1a30Sjl 	if (flags & MC_INJECT_FLAG_LD) {
3202cfb9e062Shyw 		if (flags & MC_INJECT_FLAG_PREFETCH) {
3203cfb9e062Shyw 			/*
3204cfb9e062Shyw 			 * Use strong prefetch operation to
3205cfb9e062Shyw 			 * inject MI errors.
3206cfb9e062Shyw 			 */
3207cfb9e062Shyw 			page_t *pp;
3208cfb9e062Shyw 			extern void mc_prefetch(caddr_t);
3209cfb9e062Shyw 
3210cfb9e062Shyw 			MC_LOG("prefetch\n");
3211cfb9e062Shyw 
3212cfb9e062Shyw 			pp = page_numtopp_nolock(pa >> PAGESHIFT);
3213cfb9e062Shyw 			if (pp != NULL) {
3214cfb9e062Shyw 				caddr_t	va, va1;
3215cfb9e062Shyw 
3216cfb9e062Shyw 				va = ppmapin(pp, PROT_READ|PROT_WRITE,
3217d8a0cca9Swh 				    (caddr_t)-1);
3218cfb9e062Shyw 				kpreempt_disable();
3219cfb9e062Shyw 				mc_lock_va((uint64_t)pa, va);
3220cfb9e062Shyw 				va1 = va + (pa & (PAGESIZE - 1));
3221cfb9e062Shyw 				mc_prefetch(va1);
3222cfb9e062Shyw 				mc_unlock_va(va);
3223cfb9e062Shyw 				kpreempt_enable();
3224cfb9e062Shyw 				ppmapout(va);
3225cfb9e062Shyw 
3226cfb9e062Shyw 				/*
3227cfb9e062Shyw 				 * For MI errors, we need one extra
3228cfb9e062Shyw 				 * injection for HW patrol to stop.
3229cfb9e062Shyw 				 */
3230cfb9e062Shyw 				extra_injection_needed = 1;
323125cf1a30Sjl 			} else {
3232cfb9e062Shyw 				cmn_err(CE_WARN, "Cannot find page structure"
3233d8a0cca9Swh 				    " for PA %lx\n", pa);
323425cf1a30Sjl 			}
323525cf1a30Sjl 		} else {
323625cf1a30Sjl 			MC_LOG("Reading from %lx\n", pa);
323725cf1a30Sjl 			data = ldphys(pa);
323825cf1a30Sjl 			MC_LOG("data = %x\n", data);
323925cf1a30Sjl 		}
3240cfb9e062Shyw 
3241cfb9e062Shyw 		if (extra_injection_needed) {
3242cfb9e062Shyw 			/*
3243cfb9e062Shyw 			 * These are the injection cases where the
3244cfb9e062Shyw 			 * requested injected errors will not cause the HW
3245cfb9e062Shyw 			 * patrol to stop. For these cases, we need to inject
3246cfb9e062Shyw 			 * an extra 'real' PTRL error to force the
3247cfb9e062Shyw 			 * HW patrol to stop so that we can report the
3248cfb9e062Shyw 			 * errors injected. Note that we cannot read
3249cfb9e062Shyw 			 * and report error status while the HW patrol
3250cfb9e062Shyw 			 * is running.
3251cfb9e062Shyw 			 */
3252cfb9e062Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank),
3253d8a0cca9Swh 			    cntl & MAC_EG_SETUP_MASK);
3254cfb9e062Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
3255cfb9e062Shyw 
3256cfb9e062Shyw 			if (both_sides) {
3257d8a0cca9Swh 				ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
3258d8a0cca9Swh 				    MAC_EG_SETUP_MASK);
3259d8a0cca9Swh 				ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
3260cfb9e062Shyw 			}
3261cfb9e062Shyw 			data = 0xf0e0d0c0;
3262cfb9e062Shyw 			MC_LOG("Writing %x to %lx\n", data, pa);
3263cfb9e062Shyw 			stphys(pa, data);
3264cfb9e062Shyw 			cpu_flush_ecache();
3265cfb9e062Shyw 		}
326625cf1a30Sjl 	}
326725cf1a30Sjl 
326825cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESTART) {
326925cf1a30Sjl 		MC_LOG("Restart patrol\n");
3270738dd194Shyw 		rsaddr.mi_restartaddr.ma_bd = mcp->mc_board_num;
3271738dd194Shyw 		rsaddr.mi_restartaddr.ma_bank = bank;
3272738dd194Shyw 		rsaddr.mi_restartaddr.ma_dimm_addr = dimm_addr;
3273738dd194Shyw 		rsaddr.mi_valid = 1;
3274738dd194Shyw 		rsaddr.mi_injectrestart = 1;
3275738dd194Shyw 		restart_patrol(mcp, bank, &rsaddr);
327625cf1a30Sjl 	}
327725cf1a30Sjl 
327825cf1a30Sjl 	if (flags & MC_INJECT_FLAG_POLL) {
32790cc8ae86Sav 		int running;
328037afe445Shyw 		int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
328125cf1a30Sjl 
328225cf1a30Sjl 		MC_LOG("Poll patrol error\n");
328325cf1a30Sjl 		stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank));
328425cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
32850cc8ae86Sav 		running = cntl & MAC_CNTL_PTRL_START;
328625cf1a30Sjl 
3287cfb9e062Shyw 		if (!running &&
3288cfb9e062Shyw 		    (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS))) {
3289cfb9e062Shyw 			/*
3290cfb9e062Shyw 			 * HW patrol stopped and we have errors to
3291cfb9e062Shyw 			 * report. Do it.
3292cfb9e062Shyw 			 */
329337afe445Shyw 			mcp->mc_speedup_period[ebank] = 0;
3294738dd194Shyw 			rsaddr.mi_valid = 0;
3295738dd194Shyw 			rsaddr.mi_injectrestart = 0;
3296738dd194Shyw 			if (IS_MIRROR(mcp, bank)) {
3297738dd194Shyw 				mc_error_handler_mir(mcp, bank, &rsaddr);
3298738dd194Shyw 			} else {
3299738dd194Shyw 				mc_error_handler(mcp, bank, &rsaddr);
3300738dd194Shyw 			}
3301cfb9e062Shyw 
3302738dd194Shyw 			restart_patrol(mcp, bank, &rsaddr);
3303cfb9e062Shyw 		} else {
3304cfb9e062Shyw 			/*
3305cfb9e062Shyw 			 * We are expecting to report injected
3306cfb9e062Shyw 			 * errors but the HW patrol is still running.
3307cfb9e062Shyw 			 * Speed up the scanning
3308cfb9e062Shyw 			 */
330937afe445Shyw 			mcp->mc_speedup_period[ebank] = 2;
3310cfb9e062Shyw 			MAC_CMD(mcp, bank, 0);
331125cf1a30Sjl 			restart_patrol(mcp, bank, NULL);
3312cfb9e062Shyw 		}
331325cf1a30Sjl 	}
331425cf1a30Sjl 
331525cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
331625cf1a30Sjl 	return (0);
331725cf1a30Sjl }
3318cfb9e062Shyw 
331925cf1a30Sjl void
332025cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data)
332125cf1a30Sjl {
332225cf1a30Sjl 	MC_LOG("0x%x -> pa(%lx)\n", data, pa);
332325cf1a30Sjl 	stphysio(pa, data);
33240cc8ae86Sav 
33250cc8ae86Sav 	/* force the above write to be processed by mac patrol */
3326cfb9e062Shyw 	data = ldphysio(pa);
3327cfb9e062Shyw 	MC_LOG("pa(%lx) = 0x%x\n", pa, data);
332825cf1a30Sjl }
332925cf1a30Sjl 
333025cf1a30Sjl uint32_t
333125cf1a30Sjl mc_ldphysio(uint64_t pa)
333225cf1a30Sjl {
333325cf1a30Sjl 	uint32_t rv;
333425cf1a30Sjl 
333525cf1a30Sjl 	rv = ldphysio(pa);
333625cf1a30Sjl 	MC_LOG("pa(%lx) = 0x%x\n", pa, rv);
333725cf1a30Sjl 	return (rv);
333825cf1a30Sjl }
33390cc8ae86Sav 
33400cc8ae86Sav #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
33410cc8ae86Sav 
33420cc8ae86Sav /*
33430cc8ae86Sav  * parse_unum_memory -- extract the board number and the DIMM name from
33440cc8ae86Sav  * the unum.
33450cc8ae86Sav  *
33460cc8ae86Sav  * Return 0 for success and non-zero for a failure.
33470cc8ae86Sav  */
33480cc8ae86Sav int
33490cc8ae86Sav parse_unum_memory(char *unum, int *board, char *dname)
33500cc8ae86Sav {
33510cc8ae86Sav 	char *c;
33520cc8ae86Sav 	char x, y, z;
33530cc8ae86Sav 
33540cc8ae86Sav 	if ((c = strstr(unum, "CMU")) != NULL) {
33550cc8ae86Sav 		/* DC Model */
33560cc8ae86Sav 		c += 3;
33570cc8ae86Sav 		*board = (uint8_t)stoi(&c);
33580cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
33590cc8ae86Sav 			return (1);
33600cc8ae86Sav 		}
33610cc8ae86Sav 		c += 3;
33620cc8ae86Sav 		if (strlen(c) < 3) {
33630cc8ae86Sav 			return (2);
33640cc8ae86Sav 		}
33650cc8ae86Sav 		if ((!isdigit(c[0])) || (!(isdigit(c[1]))) ||
33660cc8ae86Sav 		    ((c[2] != 'A') && (c[2] != 'B'))) {
33670cc8ae86Sav 			return (3);
33680cc8ae86Sav 		}
33690cc8ae86Sav 		x = c[0];
33700cc8ae86Sav 		y = c[1];
33710cc8ae86Sav 		z = c[2];
33720cc8ae86Sav 	} else if ((c = strstr(unum, "MBU_")) != NULL) {
33730cc8ae86Sav 		/*  FF1/FF2 Model */
33740cc8ae86Sav 		c += 4;
33750cc8ae86Sav 		if ((c[0] != 'A') && (c[0] != 'B')) {
33760cc8ae86Sav 			return (4);
33770cc8ae86Sav 		}
33780cc8ae86Sav 		if ((c = strstr(c, "MEMB")) == NULL) {
33790cc8ae86Sav 			return (5);
33800cc8ae86Sav 		}
33810cc8ae86Sav 		c += 4;
33820cc8ae86Sav 
33830cc8ae86Sav 		x = c[0];
33840cc8ae86Sav 		*board =  ((uint8_t)stoi(&c)) / 4;
33850cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
33860cc8ae86Sav 			return (6);
33870cc8ae86Sav 		}
33880cc8ae86Sav 		c += 3;
33890cc8ae86Sav 		if (strlen(c) < 2) {
33900cc8ae86Sav 			return (7);
33910cc8ae86Sav 		}
33920cc8ae86Sav 		if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) {
33930cc8ae86Sav 			return (8);
33940cc8ae86Sav 		}
33950cc8ae86Sav 		y = c[0];
33960cc8ae86Sav 		z = c[1];
33970cc8ae86Sav 	} else {
33980cc8ae86Sav 		return (9);
33990cc8ae86Sav 	}
34000cc8ae86Sav 	if (*board < 0) {
34010cc8ae86Sav 		return (10);
34020cc8ae86Sav 	}
34030cc8ae86Sav 	dname[0] = x;
34040cc8ae86Sav 	dname[1] = y;
34050cc8ae86Sav 	dname[2] = z;
34060cc8ae86Sav 	dname[3] = '\0';
34070cc8ae86Sav 	return (0);
34080cc8ae86Sav }
34090cc8ae86Sav 
34100cc8ae86Sav /*
34110cc8ae86Sav  * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and
34120cc8ae86Sav  * the DIMM name.
34130cc8ae86Sav  */
34140cc8ae86Sav int
34150cc8ae86Sav mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
34160cc8ae86Sav     int buflen, int *lenp)
34170cc8ae86Sav {
34180cc8ae86Sav 	int		ret = ENODEV;
34190cc8ae86Sav 	mc_dimm_info_t	*d = NULL;
34200cc8ae86Sav 
34210cc8ae86Sav 	if ((d = mcp->mc_dimm_list) == NULL)
34220cc8ae86Sav 		return (ENOTSUP);
34230cc8ae86Sav 
34240cc8ae86Sav 	for (; d != NULL; d = d->md_next) {
34250cc8ae86Sav 		if (strcmp(d->md_dimmname, dname) == 0) {
34260cc8ae86Sav 			break;
34270cc8ae86Sav 		}
34280cc8ae86Sav 	}
34290cc8ae86Sav 	if (d != NULL) {
34300cc8ae86Sav 		*lenp = strlen(d->md_serial) + strlen(d->md_partnum);
34310cc8ae86Sav 		if (buflen <=  *lenp) {
34320cc8ae86Sav 			cmn_err(CE_WARN, "mc_get_mem_sid_dimm: "
34330cc8ae86Sav 			    "buflen is smaller than %d\n", *lenp);
34340cc8ae86Sav 			ret = ENOSPC;
34350cc8ae86Sav 		} else {
34360cc8ae86Sav 			snprintf(buf, buflen, "%s:%s",
34370cc8ae86Sav 			    d->md_serial, d->md_partnum);
34380cc8ae86Sav 			ret = 0;
34390cc8ae86Sav 		}
34400cc8ae86Sav 	}
34410cc8ae86Sav 	MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n",
34420cc8ae86Sav 	    ret, dname, (ret == 0) ? buf : "");
34430cc8ae86Sav 	return (ret);
34440cc8ae86Sav }
34450cc8ae86Sav 
34460cc8ae86Sav int
3447aeb241b2Sav mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int sb,
34480cc8ae86Sav     int bank, uint32_t mf_type, uint32_t d_slot)
34490cc8ae86Sav {
34500cc8ae86Sav 	int	lenp = buflen;
34510cc8ae86Sav 	int	id;
34520cc8ae86Sav 	int	ret;
34530cc8ae86Sav 	char	*dimmnm;
34540cc8ae86Sav 
3455056c948bStsien 	if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
3456056c948bStsien 	    mf_type == FLT_TYPE_PERMANENT_CE) {
34570cc8ae86Sav 		if (plat_model == MODEL_DC) {
34580cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
3459aeb241b2Sav 			dimmnm = mc_dc_dimm_unum_table[id];
34600cc8ae86Sav 		} else {
34610cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
3462aeb241b2Sav 			dimmnm = mc_ff_dimm_unum_table[id];
34630cc8ae86Sav 		}
34640cc8ae86Sav 		if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen,
34650cc8ae86Sav 		    &lenp)) != 0) {
34660cc8ae86Sav 			return (ret);
34670cc8ae86Sav 		}
34680cc8ae86Sav 	} else {
34690cc8ae86Sav 		return (1);
34700cc8ae86Sav 	}
34710cc8ae86Sav 
34720cc8ae86Sav 	return (0);
34730cc8ae86Sav }
34740cc8ae86Sav 
34750cc8ae86Sav /*
34760cc8ae86Sav  * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum.
34770cc8ae86Sav  */
34780cc8ae86Sav int
34790cc8ae86Sav mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
34800cc8ae86Sav {
34810cc8ae86Sav 	int	i;
34820cc8ae86Sav 	int	ret = ENODEV;
34830cc8ae86Sav 	int	board;
34840cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
34850cc8ae86Sav 	mc_opl_t *mcp;
34860cc8ae86Sav 
34870cc8ae86Sav 	MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen);
34880cc8ae86Sav 	if ((ret = parse_unum_memory(unum, &board, dname)) != 0) {
34890cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
34900cc8ae86Sav 		    unum, ret);
34910cc8ae86Sav 		return (EINVAL);
34920cc8ae86Sav 	}
34930cc8ae86Sav 
34940cc8ae86Sav 	if (board < 0) {
34950cc8ae86Sav 		MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n",
34960cc8ae86Sav 		    board, dname);
34970cc8ae86Sav 		return (EINVAL);
34980cc8ae86Sav 	}
34990cc8ae86Sav 
35000cc8ae86Sav 	mutex_enter(&mcmutex);
35011039f409Sav 	/*
35021039f409Sav 	 * return ENOENT if we can not find the matching board.
35031039f409Sav 	 */
35041039f409Sav 	ret = ENOENT;
35050cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
35060cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
35070cc8ae86Sav 			continue;
35080cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3509aeb241b2Sav 		if (mcp->mc_phys_board_num != board) {
3510aeb241b2Sav 			mutex_exit(&mcp->mc_lock);
3511aeb241b2Sav 			continue;
3512aeb241b2Sav 		}
3513aeb241b2Sav 		ret = mc_get_mem_sid_dimm(mcp, dname, buf, buflen, lenp);
3514aeb241b2Sav 		if (ret == 0) {
35150cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
35160cc8ae86Sav 			break;
35170cc8ae86Sav 		}
35180cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
35190cc8ae86Sav 	}
35200cc8ae86Sav 	mutex_exit(&mcmutex);
35210cc8ae86Sav 	return (ret);
35220cc8ae86Sav }
35230cc8ae86Sav 
35240cc8ae86Sav /*
35250cc8ae86Sav  * mc_get_mem_offset -- get the offset in a DIMM for a given physical address.
35260cc8ae86Sav  */
35270cc8ae86Sav int
35280cc8ae86Sav mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
35290cc8ae86Sav {
35300cc8ae86Sav 	int		i;
35310cc8ae86Sav 	int		ret = ENODEV;
35320cc8ae86Sav 	mc_addr_t	maddr;
35330cc8ae86Sav 	mc_opl_t	*mcp;
35340cc8ae86Sav 
35350cc8ae86Sav 	mutex_enter(&mcmutex);
3536c964b0e6Sraghuram 	for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) {
35370cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
35380cc8ae86Sav 			continue;
35390cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
35400cc8ae86Sav 		if (!pa_is_valid(mcp, paddr)) {
35410cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
35420cc8ae86Sav 			continue;
35430cc8ae86Sav 		}
35440cc8ae86Sav 		if (pa_to_maddr(mcp, paddr, &maddr) == 0) {
35450cc8ae86Sav 			*offp = maddr.ma_dimm_addr;
35460cc8ae86Sav 			ret = 0;
35470cc8ae86Sav 		}
35480cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
35490cc8ae86Sav 	}
35500cc8ae86Sav 	mutex_exit(&mcmutex);
35510cc8ae86Sav 	MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n",
35520cc8ae86Sav 	    ret, paddr, *offp);
35530cc8ae86Sav 	return (ret);
35540cc8ae86Sav }
35550cc8ae86Sav 
35560cc8ae86Sav /*
35570cc8ae86Sav  * dname_to_bankslot - Get the bank and slot number from the DIMM name.
35580cc8ae86Sav  */
35590cc8ae86Sav int
35600cc8ae86Sav dname_to_bankslot(char *dname, int *bank, int *slot)
35610cc8ae86Sav {
35620cc8ae86Sav 	int i;
35630cc8ae86Sav 	int tsz;
35640cc8ae86Sav 	char **tbl;
35650cc8ae86Sav 
35660cc8ae86Sav 	if (plat_model == MODEL_DC) { /* DC */
35670cc8ae86Sav 		tbl = mc_dc_dimm_unum_table;
35680cc8ae86Sav 		tsz = OPL_MAX_DIMMS;
35690cc8ae86Sav 	} else {
35700cc8ae86Sav 		tbl = mc_ff_dimm_unum_table;
35710cc8ae86Sav 		tsz = 2 * OPL_MAX_DIMMS;
35720cc8ae86Sav 	}
35730cc8ae86Sav 
35740cc8ae86Sav 	for (i = 0; i < tsz; i++) {
35750cc8ae86Sav 		if (strcmp(dname,  tbl[i]) == 0) {
35760cc8ae86Sav 			break;
35770cc8ae86Sav 		}
35780cc8ae86Sav 	}
35790cc8ae86Sav 	if (i == tsz) {
35800cc8ae86Sav 		return (1);
35810cc8ae86Sav 	}
35820cc8ae86Sav 	*bank = INDEX_TO_BANK(i);
35830cc8ae86Sav 	*slot = INDEX_TO_SLOT(i);
35840cc8ae86Sav 	return (0);
35850cc8ae86Sav }
35860cc8ae86Sav 
35870cc8ae86Sav /*
35880cc8ae86Sav  * mc_get_mem_addr -- get the physical address of a DIMM corresponding
35890cc8ae86Sav  * to the unum and sid.
35900cc8ae86Sav  */
35910cc8ae86Sav int
35920cc8ae86Sav mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr)
35930cc8ae86Sav {
35940cc8ae86Sav 	int	board;
35950cc8ae86Sav 	int	bank;
35960cc8ae86Sav 	int	slot;
35970cc8ae86Sav 	int	i;
35980cc8ae86Sav 	int	ret = ENODEV;
35990cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
36000cc8ae86Sav 	mc_addr_t maddr;
36010cc8ae86Sav 	mc_opl_t *mcp;
36020cc8ae86Sav 
36030cc8ae86Sav 	MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n",
36040cc8ae86Sav 	    unum, sid, offset);
36050cc8ae86Sav 	if (parse_unum_memory(unum, &board, dname) != 0) {
36060cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
36070cc8ae86Sav 		    unum, ret);
36080cc8ae86Sav 		return (EINVAL);
36090cc8ae86Sav 	}
36100cc8ae86Sav 
36110cc8ae86Sav 	if (board < 0) {
36120cc8ae86Sav 		MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n",
36130cc8ae86Sav 		    board, dname);
36140cc8ae86Sav 		return (EINVAL);
36150cc8ae86Sav 	}
36160cc8ae86Sav 
36170cc8ae86Sav 	mutex_enter(&mcmutex);
36180cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
36190cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
36200cc8ae86Sav 			continue;
36210cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3622aeb241b2Sav 		if (mcp->mc_phys_board_num != board) {
36230cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
36240cc8ae86Sav 			continue;
36250cc8ae86Sav 		}
36260cc8ae86Sav 
36270cc8ae86Sav 		ret = dname_to_bankslot(dname, &bank, &slot);
36280cc8ae86Sav 		MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot);
36290cc8ae86Sav 		if (ret != 0) {
36300cc8ae86Sav 			MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n");
36310cc8ae86Sav 			ret = ENODEV;
36320cc8ae86Sav 		} else {
3633aeb241b2Sav 			maddr.ma_bd = mcp->mc_board_num;
36340cc8ae86Sav 			maddr.ma_bank =  bank;
36350cc8ae86Sav 			maddr.ma_dimm_addr = offset;
36360cc8ae86Sav 			ret = mcaddr_to_pa(mcp, &maddr, paddr);
36370cc8ae86Sav 			if (ret != 0) {
36380cc8ae86Sav 				MC_LOG("mc_get_mem_addr: "
36390cc8ae86Sav 				    "mcaddr_to_pa failed\n");
36400cc8ae86Sav 				ret = ENODEV;
36410cc8ae86Sav 			}
3642aeb241b2Sav 			mutex_exit(&mcp->mc_lock);
3643aeb241b2Sav 			break;
36440cc8ae86Sav 		}
36450cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
36460cc8ae86Sav 	}
36470cc8ae86Sav 	mutex_exit(&mcmutex);
36480cc8ae86Sav 	MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr);
36490cc8ae86Sav 	return (ret);
36500cc8ae86Sav }
36510cc8ae86Sav 
36520cc8ae86Sav static void
36530cc8ae86Sav mc_free_dimm_list(mc_dimm_info_t *d)
36540cc8ae86Sav {
36550cc8ae86Sav 	mc_dimm_info_t *next;
36560cc8ae86Sav 
36570cc8ae86Sav 	while (d != NULL) {
36580cc8ae86Sav 		next = d->md_next;
36590cc8ae86Sav 		kmem_free(d, sizeof (mc_dimm_info_t));
36600cc8ae86Sav 		d = next;
36610cc8ae86Sav 	}
36620cc8ae86Sav }
36630cc8ae86Sav 
36640cc8ae86Sav /*
36650cc8ae86Sav  * mc_get_dimm_list -- get the list of dimms with serial-id info
36660cc8ae86Sav  * from the SP.
36670cc8ae86Sav  */
36680cc8ae86Sav mc_dimm_info_t *
36690cc8ae86Sav mc_get_dimm_list(mc_opl_t *mcp)
36700cc8ae86Sav {
36710cc8ae86Sav 	uint32_t	bufsz;
36720cc8ae86Sav 	uint32_t	maxbufsz;
36730cc8ae86Sav 	int		ret;
36740cc8ae86Sav 	int		sexp;
36750cc8ae86Sav 	board_dimm_info_t *bd_dimmp;
36760cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
36770cc8ae86Sav 
36780cc8ae86Sav 	maxbufsz = bufsz = sizeof (board_dimm_info_t) +
36790cc8ae86Sav 	    ((MCOPL_MAX_DIMMNAME +  MCOPL_MAX_SERIAL +
36800cc8ae86Sav 	    MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS);
36810cc8ae86Sav 
36820cc8ae86Sav 	bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP);
36830cc8ae86Sav 	ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz);
36840cc8ae86Sav 
36850cc8ae86Sav 	MC_LOG("mc_get_dimm_list:  scf_service_getinfo returned=%d\n", ret);
36860cc8ae86Sav 	if (ret == 0) {
36870cc8ae86Sav 		sexp = sizeof (board_dimm_info_t) +
36880cc8ae86Sav 		    ((bd_dimmp->bd_dnamesz +  bd_dimmp->bd_serialsz +
36890cc8ae86Sav 		    bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms);
36900cc8ae86Sav 
36910cc8ae86Sav 		if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) &&
36920cc8ae86Sav 		    (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) &&
36930cc8ae86Sav 		    (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) &&
36940cc8ae86Sav 		    (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) &&
36950cc8ae86Sav 		    (sexp <= bufsz)) {
36960cc8ae86Sav 
36970cc8ae86Sav #ifdef DEBUG
36980cc8ae86Sav 			if (oplmc_debug)
36990cc8ae86Sav 				mc_dump_dimm_info(bd_dimmp);
37000cc8ae86Sav #endif
37010cc8ae86Sav 			dimm_list = mc_prepare_dimmlist(bd_dimmp);
37020cc8ae86Sav 
37030cc8ae86Sav 		} else {
37040cc8ae86Sav 			cmn_err(CE_WARN, "DIMM info version mismatch\n");
37050cc8ae86Sav 		}
37060cc8ae86Sav 	}
37070cc8ae86Sav 	kmem_free(bd_dimmp, maxbufsz);
37080cc8ae86Sav 	MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list);
37090cc8ae86Sav 	return (dimm_list);
37100cc8ae86Sav }
37110cc8ae86Sav 
37120cc8ae86Sav /*
37131039f409Sav  * mc_prepare_dimmlist - Prepare the dimm list from the information
37141039f409Sav  * received from the SP.
37150cc8ae86Sav  */
37160cc8ae86Sav mc_dimm_info_t *
37170cc8ae86Sav mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp)
37180cc8ae86Sav {
37190cc8ae86Sav 	char	*dimm_name;
37200cc8ae86Sav 	char	*serial;
37210cc8ae86Sav 	char	*part;
37220cc8ae86Sav 	int	dimm;
37230cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
37240cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
37250cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
37260cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
37270cc8ae86Sav 	mc_dimm_info_t	*d;
37280cc8ae86Sav 
37290cc8ae86Sav 	dimm_name = (char *)(bd_dimmp + 1);
37300cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
37310cc8ae86Sav 
37320cc8ae86Sav 		d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t),
37330cc8ae86Sav 		    KM_SLEEP);
3734ad59b69dSbm 
3735ad59b69dSbm 		bcopy(dimm_name, d->md_dimmname, dnamesz);
3736ad59b69dSbm 		d->md_dimmname[dnamesz] = 0;
3737ad59b69dSbm 
37380cc8ae86Sav 		serial = dimm_name + dnamesz;
3739ad59b69dSbm 		bcopy(serial, d->md_serial, sersz);
3740ad59b69dSbm 		d->md_serial[sersz] = 0;
3741ad59b69dSbm 
37420cc8ae86Sav 		part = serial + sersz;
3743ad59b69dSbm 		bcopy(part, d->md_partnum, partsz);
3744ad59b69dSbm 		d->md_partnum[partsz] = 0;
37450cc8ae86Sav 
37460cc8ae86Sav 		d->md_next = dimm_list;
37470cc8ae86Sav 		dimm_list = d;
37480cc8ae86Sav 		dimm_name = part + partsz;
37490cc8ae86Sav 	}
37500cc8ae86Sav 	return (dimm_list);
37510cc8ae86Sav }
37520cc8ae86Sav 
37530cc8ae86Sav #ifdef DEBUG
37540cc8ae86Sav void
37550cc8ae86Sav mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz)
37560cc8ae86Sav {
37570cc8ae86Sav 	char dname[MCOPL_MAX_DIMMNAME + 1];
37580cc8ae86Sav 	char serial[MCOPL_MAX_SERIAL + 1];
37590cc8ae86Sav 	char part[ MCOPL_MAX_PARTNUM + 1];
37600cc8ae86Sav 	char *b;
37610cc8ae86Sav 
37620cc8ae86Sav 	b = buf;
3763ad59b69dSbm 	bcopy(b, dname, dnamesz);
3764ad59b69dSbm 	dname[dnamesz] = 0;
3765ad59b69dSbm 
37660cc8ae86Sav 	b += dnamesz;
3767ad59b69dSbm 	bcopy(b, serial, serialsz);
3768ad59b69dSbm 	serial[serialsz] = 0;
3769ad59b69dSbm 
37700cc8ae86Sav 	b += serialsz;
3771ad59b69dSbm 	bcopy(b, part, partnumsz);
3772ad59b69dSbm 	part[partnumsz] = 0;
3773ad59b69dSbm 
37740cc8ae86Sav 	printf("DIMM=%s  Serial=%s PartNum=%s\n", dname, serial, part);
37750cc8ae86Sav }
37760cc8ae86Sav 
37770cc8ae86Sav void
37780cc8ae86Sav mc_dump_dimm_info(board_dimm_info_t *bd_dimmp)
37790cc8ae86Sav {
37800cc8ae86Sav 	int	dimm;
37810cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
37820cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
37830cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
37840cc8ae86Sav 	char	*buf;
37850cc8ae86Sav 
37860cc8ae86Sav 	printf("Version=%d Board=%02d DIMMs=%d NameSize=%d "
37870cc8ae86Sav 	    "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version,
37880cc8ae86Sav 	    bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz,
37890cc8ae86Sav 	    bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz);
37900cc8ae86Sav 	printf("======================================================\n");
37910cc8ae86Sav 
37920cc8ae86Sav 	buf = (char *)(bd_dimmp + 1);
37930cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
37940cc8ae86Sav 		mc_dump_dimm(buf, dnamesz, sersz, partsz);
37950cc8ae86Sav 		buf += dnamesz + sersz + partsz;
37960cc8ae86Sav 	}
37970cc8ae86Sav 	printf("======================================================\n");
37980cc8ae86Sav }
37990cc8ae86Sav 
38000cc8ae86Sav 
38010cc8ae86Sav /* ARGSUSED */
38020cc8ae86Sav static int
38030cc8ae86Sav mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
38040cc8ae86Sav 	int *rvalp)
38050cc8ae86Sav {
38060cc8ae86Sav 	caddr_t	buf;
38070cc8ae86Sav 	uint64_t pa;
38080cc8ae86Sav 	int rv = 0;
38090cc8ae86Sav 	int i;
38100cc8ae86Sav 	uint32_t flags;
38110cc8ae86Sav 	static uint32_t offset = 0;
38120cc8ae86Sav 
38130cc8ae86Sav 
38140cc8ae86Sav 	flags = (cmd >> 4) & 0xfffffff;
38150cc8ae86Sav 
38160cc8ae86Sav 	cmd &= 0xf;
38170cc8ae86Sav 
38180cc8ae86Sav 	MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags);
38190cc8ae86Sav 
38200cc8ae86Sav 	if (arg != NULL) {
38210cc8ae86Sav 		if (ddi_copyin((const void *)arg, (void *)&pa,
3822d8a0cca9Swh 		    sizeof (uint64_t), 0) < 0) {
38230cc8ae86Sav 			rv = EFAULT;
38240cc8ae86Sav 			return (rv);
38250cc8ae86Sav 		}
38260cc8ae86Sav 		buf = NULL;
38270cc8ae86Sav 	} else {
38280cc8ae86Sav 		buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP);
38290cc8ae86Sav 
38300cc8ae86Sav 		pa = va_to_pa(buf);
38310cc8ae86Sav 		pa += offset;
38320cc8ae86Sav 
38330cc8ae86Sav 		offset += 64;
38340cc8ae86Sav 		if (offset >= PAGESIZE)
38350cc8ae86Sav 			offset = 0;
38360cc8ae86Sav 	}
38370cc8ae86Sav 
38380cc8ae86Sav 	switch (cmd) {
38390cc8ae86Sav 	case MCI_CE:
3840d8a0cca9Swh 		mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa, flags);
38410cc8ae86Sav 		break;
38420cc8ae86Sav 	case MCI_PERM_CE:
3843d8a0cca9Swh 		mc_inject_error(MC_INJECT_PERMANENT_CE, pa, flags);
38440cc8ae86Sav 		break;
38450cc8ae86Sav 	case MCI_UE:
3846d8a0cca9Swh 		mc_inject_error(MC_INJECT_UE, pa, flags);
38470cc8ae86Sav 		break;
38480cc8ae86Sav 	case MCI_M_CE:
3849d8a0cca9Swh 		mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa, flags);
38500cc8ae86Sav 		break;
38510cc8ae86Sav 	case MCI_M_PCE:
3852d8a0cca9Swh 		mc_inject_error(MC_INJECT_PERMANENT_MCE, pa, flags);
38530cc8ae86Sav 		break;
38540cc8ae86Sav 	case MCI_M_UE:
3855d8a0cca9Swh 		mc_inject_error(MC_INJECT_MUE, pa, flags);
38560cc8ae86Sav 		break;
38570cc8ae86Sav 	case MCI_CMP:
3858d8a0cca9Swh 		mc_inject_error(MC_INJECT_CMPE, pa, flags);
38590cc8ae86Sav 		break;
38600cc8ae86Sav 	case MCI_NOP:
3861d8a0cca9Swh 		mc_inject_error(MC_INJECT_NOP, pa, flags); break;
38620cc8ae86Sav 	case MCI_SHOW_ALL:
38630cc8ae86Sav 		mc_debug_show_all = 1;
38640cc8ae86Sav 		break;
38650cc8ae86Sav 	case MCI_SHOW_NONE:
38660cc8ae86Sav 		mc_debug_show_all = 0;
38670cc8ae86Sav 		break;
38680cc8ae86Sav 	case MCI_ALLOC:
38690cc8ae86Sav 		/*
38700cc8ae86Sav 		 * just allocate some kernel memory and never free it
38710cc8ae86Sav 		 * 512 MB seems to be the maximum size supported.
38720cc8ae86Sav 		 */
38730cc8ae86Sav 		cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512);
38740cc8ae86Sav 		for (i = 0; i < flags; i++) {
38750cc8ae86Sav 			buf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP);
38760cc8ae86Sav 			cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n",
3877d8a0cca9Swh 			    (u_longlong_t)buf, (u_longlong_t)va_to_pa(buf));
38780cc8ae86Sav 		}
38790cc8ae86Sav 		break;
38800cc8ae86Sav 	case MCI_SUSPEND:
38810cc8ae86Sav 		(void) opl_mc_suspend();
38820cc8ae86Sav 		break;
38830cc8ae86Sav 	case MCI_RESUME:
38840cc8ae86Sav 		(void) opl_mc_resume();
38850cc8ae86Sav 		break;
38860cc8ae86Sav 	default:
38870cc8ae86Sav 		rv = ENXIO;
38880cc8ae86Sav 	}
38890cc8ae86Sav 	return (rv);
38900cc8ae86Sav }
38910cc8ae86Sav 
38920cc8ae86Sav #endif /* DEBUG */
3893