xref: /illumos-gate/usr/src/uts/sun4u/opl/io/mc-opl.c (revision aeb241b2)
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 /*
221e2e7a75Shuah  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
231e2e7a75Shuah  * Use is subject to license terms.
241e2e7a75Shuah  */
2525cf1a30Sjl /*
2625cf1a30Sjl  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
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>
4325cf1a30Sjl #include <sys/ddifm.h>
4425cf1a30Sjl #include <sys/fm/protocol.h>
4525cf1a30Sjl #include <sys/fm/util.h>
4625cf1a30Sjl #include <sys/kmem.h>
4725cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h>
4825cf1a30Sjl #include <sys/memlist.h>
4925cf1a30Sjl #include <sys/param.h>
500cc8ae86Sav #include <sys/disp.h>
5125cf1a30Sjl #include <vm/page.h>
5225cf1a30Sjl #include <sys/mc-opl.h>
530cc8ae86Sav #include <sys/opl.h>
540cc8ae86Sav #include <sys/opl_dimm.h>
550cc8ae86Sav #include <sys/scfd/scfostoescf.h>
56cfb9e062Shyw #include <sys/cpu_module.h>
57cfb9e062Shyw #include <vm/seg_kmem.h>
58cfb9e062Shyw #include <sys/vmem.h>
59cfb9e062Shyw #include <vm/hat_sfmmu.h>
60cfb9e062Shyw #include <sys/vmsystm.h>
61738dd194Shyw #include <sys/membar.h>
6225cf1a30Sjl 
6325cf1a30Sjl /*
6425cf1a30Sjl  * Function prototypes
6525cf1a30Sjl  */
6625cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *);
6725cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *);
6825cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
6925cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
7025cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
7125cf1a30Sjl 
720cc8ae86Sav static int mc_poll_init(void);
730cc8ae86Sav static void mc_poll_fini(void);
7425cf1a30Sjl static int mc_board_add(mc_opl_t *mcp);
7525cf1a30Sjl static int mc_board_del(mc_opl_t *mcp);
7625cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag);
7725cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag);
780cc8ae86Sav int opl_mc_suspend(void);
790cc8ae86Sav int opl_mc_resume(void);
8025cf1a30Sjl 
8125cf1a30Sjl static void insert_mcp(mc_opl_t *mcp);
8225cf1a30Sjl static void delete_mcp(mc_opl_t *mcp);
8325cf1a30Sjl 
8425cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr);
8525cf1a30Sjl 
86738dd194Shyw static int mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa);
8725cf1a30Sjl 
8825cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *);
890cc8ae86Sav int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr);
900cc8ae86Sav int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
910cc8ae86Sav int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp);
920cc8ae86Sav int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
930cc8ae86Sav     int buflen, int *lenp);
940cc8ae86Sav mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp);
950cc8ae86Sav mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp);
960cc8ae86Sav int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank,
970cc8ae86Sav     uint32_t mf_type, uint32_t d_slot);
980cc8ae86Sav static void mc_free_dimm_list(mc_dimm_info_t *d);
9925cf1a30Sjl static void mc_get_mlist(mc_opl_t *);
1000cc8ae86Sav static void mc_polling(void);
1010cc8ae86Sav static int mc_opl_get_physical_board(int);
1020cc8ae86Sav 
1030cc8ae86Sav #ifdef	DEBUG
1040cc8ae86Sav static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *);
1050cc8ae86Sav void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz);
1060cc8ae86Sav void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp);
1070cc8ae86Sav #endif
10825cf1a30Sjl 
10925cf1a30Sjl #pragma weak opl_get_physical_board
11025cf1a30Sjl extern int opl_get_physical_board(int);
1110cc8ae86Sav extern int plat_max_boards(void);
11225cf1a30Sjl 
11325cf1a30Sjl /*
11425cf1a30Sjl  * Configuration data structures
11525cf1a30Sjl  */
11625cf1a30Sjl static struct cb_ops mc_cb_ops = {
11725cf1a30Sjl 	mc_open,			/* open */
11825cf1a30Sjl 	mc_close,			/* close */
11925cf1a30Sjl 	nulldev,			/* strategy */
12025cf1a30Sjl 	nulldev,			/* print */
12125cf1a30Sjl 	nodev,				/* dump */
12225cf1a30Sjl 	nulldev,			/* read */
12325cf1a30Sjl 	nulldev,			/* write */
12425cf1a30Sjl 	mc_ioctl,			/* ioctl */
12525cf1a30Sjl 	nodev,				/* devmap */
12625cf1a30Sjl 	nodev,				/* mmap */
12725cf1a30Sjl 	nodev,				/* segmap */
12825cf1a30Sjl 	nochpoll,			/* poll */
12925cf1a30Sjl 	ddi_prop_op,			/* cb_prop_op */
13025cf1a30Sjl 	0,				/* streamtab */
13125cf1a30Sjl 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
13225cf1a30Sjl 	CB_REV,				/* rev */
13325cf1a30Sjl 	nodev,				/* cb_aread */
13425cf1a30Sjl 	nodev				/* cb_awrite */
13525cf1a30Sjl };
13625cf1a30Sjl 
13725cf1a30Sjl static struct dev_ops mc_ops = {
13825cf1a30Sjl 	DEVO_REV,			/* rev */
13925cf1a30Sjl 	0,				/* refcnt  */
14025cf1a30Sjl 	ddi_getinfo_1to1,		/* getinfo */
14125cf1a30Sjl 	nulldev,			/* identify */
14225cf1a30Sjl 	nulldev,			/* probe */
14325cf1a30Sjl 	mc_attach,			/* attach */
14425cf1a30Sjl 	mc_detach,			/* detach */
14525cf1a30Sjl 	nulldev,			/* reset */
14625cf1a30Sjl 	&mc_cb_ops,			/* cb_ops */
14725cf1a30Sjl 	(struct bus_ops *)0,		/* bus_ops */
14825cf1a30Sjl 	nulldev				/* power */
14925cf1a30Sjl };
15025cf1a30Sjl 
15125cf1a30Sjl /*
15225cf1a30Sjl  * Driver globals
15325cf1a30Sjl  */
15425cf1a30Sjl 
1550cc8ae86Sav static enum {
1560cc8ae86Sav 	MODEL_FF1 = 0,
1570cc8ae86Sav 	MODEL_FF2 = 1,
1580cc8ae86Sav 	MODEL_DC = 2
1590cc8ae86Sav } plat_model = MODEL_DC;	/* The default behaviour is DC */
1600cc8ae86Sav 
1610cc8ae86Sav static struct plat_model_names {
1620cc8ae86Sav 	const char *unit_name;
1630cc8ae86Sav 	const char *mem_name;
1640cc8ae86Sav } model_names[] = {
1650cc8ae86Sav 	{ "MBU_A", "MEMB" },
1660cc8ae86Sav 	{ "MBU_B", "MEMB" },
1670cc8ae86Sav 	{ "CMU", "" }
1680cc8ae86Sav };
16925cf1a30Sjl 
1700cc8ae86Sav /*
1710cc8ae86Sav  * The DIMM Names for DC platform.
1720cc8ae86Sav  * The index into this table is made up of (bank, dslot),
1730cc8ae86Sav  * Where dslot occupies bits 0-1 and bank occupies 2-4.
1740cc8ae86Sav  */
1750cc8ae86Sav static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = {
1760cc8ae86Sav 	/* --------CMUnn----------- */
1770cc8ae86Sav 	/* --CS0-----|--CS1------ */
1780cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
179c964b0e6Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
180c964b0e6Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */
181c964b0e6Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */
182c964b0e6Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */
183c964b0e6Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */
184c964b0e6Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */
185c964b0e6Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */
186c964b0e6Sraghuram 	"31A", "30A", "31B", "30B"  /* Bank 7 (MAC 3 bank 1) */
1870cc8ae86Sav };
1880cc8ae86Sav 
1890cc8ae86Sav /*
1900cc8ae86Sav  * The DIMM Names for FF1/FF2 platforms.
1910cc8ae86Sav  * The index into this table is made up of (board, bank, dslot),
1920cc8ae86Sav  * Where dslot occupies bits 0-1, bank occupies 2-4 and
1930cc8ae86Sav  * board occupies the bit 5.
1940cc8ae86Sav  */
1950cc8ae86Sav static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = {
1960cc8ae86Sav 	/* --------CMU0---------- */
1970cc8ae86Sav 	/* --CS0-----|--CS1------ */
1980cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
199c964b0e6Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
200c964b0e6Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */
201c964b0e6Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */
202c964b0e6Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */
203c964b0e6Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */
204c964b0e6Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */
205c964b0e6Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */
206c964b0e6Sraghuram 	"31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */
2070cc8ae86Sav 	/* --------CMU1---------- */
2080cc8ae86Sav 	/* --CS0-----|--CS1------ */
2090cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
210c964b0e6Sraghuram 	"43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */
211c964b0e6Sraghuram 	"41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */
212c964b0e6Sraghuram 	"53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */
213c964b0e6Sraghuram 	"51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */
214c964b0e6Sraghuram 	"63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */
215c964b0e6Sraghuram 	"61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */
216c964b0e6Sraghuram 	"73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */
217c964b0e6Sraghuram 	"71A", "70A", "71B", "70B"  /* Bank 7 (MAC 3 bank 1) */
2180cc8ae86Sav };
2190cc8ae86Sav 
2200cc8ae86Sav #define	BD_BK_SLOT_TO_INDEX(bd, bk, s)			\
2210cc8ae86Sav 	(((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03))
2220cc8ae86Sav 
2230cc8ae86Sav #define	INDEX_TO_BANK(i)			(((i) & 0x1C) >> 2)
2240cc8ae86Sav #define	INDEX_TO_SLOT(i)			((i) & 0x03)
2250cc8ae86Sav 
226*aeb241b2Sav #define	SLOT_TO_CS(slot)	((slot & 0x3) >> 1)
227*aeb241b2Sav 
2280cc8ae86Sav /* Isolation unit size is 64 MB */
2290cc8ae86Sav #define	MC_ISOLATION_BSIZE	(64 * 1024 * 1024)
2300cc8ae86Sav 
2310cc8ae86Sav #define	MC_MAX_SPEEDS 7
2320cc8ae86Sav 
2330cc8ae86Sav typedef struct {
2340cc8ae86Sav 	uint32_t mc_speeds;
2350cc8ae86Sav 	uint32_t mc_period;
2360cc8ae86Sav } mc_scan_speed_t;
2370cc8ae86Sav 
2380cc8ae86Sav #define	MC_CNTL_SPEED_SHIFT 26
2390cc8ae86Sav 
24037afe445Shyw /*
24137afe445Shyw  * In mirror mode, we normalized the bank idx to "even" since
24237afe445Shyw  * the HW treats them as one unit w.r.t programming.
24337afe445Shyw  * This bank index will be the "effective" bank index.
24437afe445Shyw  * All mirrored bank state info on mc_period, mc_speedup_period
24537afe445Shyw  * will be stored in the even bank structure to avoid code duplication.
24637afe445Shyw  */
24737afe445Shyw #define	MIRROR_IDX(bankidx)	(bankidx & ~1)
24837afe445Shyw 
2490cc8ae86Sav static mc_scan_speed_t	mc_scan_speeds[MC_MAX_SPEEDS] = {
2500cc8ae86Sav 	{0x6 << MC_CNTL_SPEED_SHIFT, 0},
2510cc8ae86Sav 	{0x5 << MC_CNTL_SPEED_SHIFT, 32},
2520cc8ae86Sav 	{0x4 << MC_CNTL_SPEED_SHIFT, 64},
2530cc8ae86Sav 	{0x3 << MC_CNTL_SPEED_SHIFT, 128},
2540cc8ae86Sav 	{0x2 << MC_CNTL_SPEED_SHIFT, 256},
2550cc8ae86Sav 	{0x1 << MC_CNTL_SPEED_SHIFT, 512},
2560cc8ae86Sav 	{0x0 << MC_CNTL_SPEED_SHIFT, 1024}
2570cc8ae86Sav };
2580cc8ae86Sav 
2590cc8ae86Sav static uint32_t	mc_max_speed = (0x6 << 26);
2600cc8ae86Sav 
2610cc8ae86Sav int mc_isolation_bsize = MC_ISOLATION_BSIZE;
2620cc8ae86Sav int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC;
2630cc8ae86Sav int mc_max_scf_retry = 16;
2640cc8ae86Sav int mc_max_scf_logs = 64;
2650cc8ae86Sav int mc_max_errlog_processed = BANKNUM_PER_SB*2;
2660cc8ae86Sav int mc_scan_period = 12 * 60 * 60;	/* 12 hours period */
2670cc8ae86Sav int mc_max_rewrite_loop = 100;
2680cc8ae86Sav int mc_rewrite_delay = 10;
2690cc8ae86Sav /*
2700cc8ae86Sav  * it takes SCF about 300 m.s. to process a requst.  We can bail out
2710cc8ae86Sav  * if it is busy.  It does not pay to wait for it too long.
2720cc8ae86Sav  */
2730cc8ae86Sav int mc_max_scf_loop = 2;
2740cc8ae86Sav int mc_scf_delay = 100;
2750cc8ae86Sav int mc_pce_dropped = 0;
2760cc8ae86Sav int mc_poll_priority = MINCLSYSPRI;
27725cf1a30Sjl 
2780cc8ae86Sav 
2790cc8ae86Sav /*
2800cc8ae86Sav  * Mutex heierachy in mc-opl
2810cc8ae86Sav  * If both mcmutex and mc_lock must be held,
2820cc8ae86Sav  * mcmutex must be acquired first, and then mc_lock.
2830cc8ae86Sav  */
2840cc8ae86Sav 
2850cc8ae86Sav static kmutex_t mcmutex;
2860cc8ae86Sav mc_opl_t *mc_instances[OPL_MAX_BOARDS];
2870cc8ae86Sav 
2880cc8ae86Sav static kmutex_t mc_polling_lock;
2890cc8ae86Sav static kcondvar_t mc_polling_cv;
2900cc8ae86Sav static kcondvar_t mc_poll_exit_cv;
2910cc8ae86Sav static int mc_poll_cmd = 0;
2920cc8ae86Sav static int mc_pollthr_running = 0;
2930cc8ae86Sav int mc_timeout_period = 0; /* this is in m.s. */
29425cf1a30Sjl void *mc_statep;
29525cf1a30Sjl 
29625cf1a30Sjl #ifdef	DEBUG
2972742aa22Shyw int oplmc_debug = 0;
29825cf1a30Sjl #endif
29925cf1a30Sjl 
3000cc8ae86Sav static int mc_debug_show_all = 0;
30125cf1a30Sjl 
30225cf1a30Sjl extern struct mod_ops mod_driverops;
30325cf1a30Sjl 
30425cf1a30Sjl static struct modldrv modldrv = {
30525cf1a30Sjl 	&mod_driverops,			/* module type, this one is a driver */
3060cc8ae86Sav 	"OPL Memory-controller %I%",	/* module name */
30725cf1a30Sjl 	&mc_ops,			/* driver ops */
30825cf1a30Sjl };
30925cf1a30Sjl 
31025cf1a30Sjl static struct modlinkage modlinkage = {
31125cf1a30Sjl 	MODREV_1,		/* rev */
31225cf1a30Sjl 	(void *)&modldrv,
31325cf1a30Sjl 	NULL
31425cf1a30Sjl };
31525cf1a30Sjl 
31625cf1a30Sjl #pragma weak opl_get_mem_unum
3170cc8ae86Sav #pragma weak opl_get_mem_sid
3180cc8ae86Sav #pragma weak opl_get_mem_offset
3190cc8ae86Sav #pragma weak opl_get_mem_addr
3200cc8ae86Sav 
32125cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *);
3220cc8ae86Sav extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp);
3230cc8ae86Sav extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp);
3240cc8ae86Sav extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset,
3250cc8ae86Sav     uint64_t *paddr);
3260cc8ae86Sav 
32725cf1a30Sjl 
32825cf1a30Sjl /*
32925cf1a30Sjl  * pseudo-mc node portid format
33025cf1a30Sjl  *
33125cf1a30Sjl  *		[10]   = 0
33225cf1a30Sjl  *		[9]    = 1
33325cf1a30Sjl  *		[8]    = LSB_ID[4] = 0
33425cf1a30Sjl  *		[7:4]  = LSB_ID[3:0]
33525cf1a30Sjl  *		[3:0]  = 0
33625cf1a30Sjl  *
33725cf1a30Sjl  */
33825cf1a30Sjl 
33925cf1a30Sjl /*
34025cf1a30Sjl  * These are the module initialization routines.
34125cf1a30Sjl  */
34225cf1a30Sjl int
34325cf1a30Sjl _init(void)
34425cf1a30Sjl {
3450cc8ae86Sav 	int	error;
3460cc8ae86Sav 	int	plen;
3470cc8ae86Sav 	char	model[20];
3480cc8ae86Sav 	pnode_t	node;
34925cf1a30Sjl 
35025cf1a30Sjl 
35125cf1a30Sjl 	if ((error = ddi_soft_state_init(&mc_statep,
35225cf1a30Sjl 	    sizeof (mc_opl_t), 1)) != 0)
35325cf1a30Sjl 		return (error);
35425cf1a30Sjl 
3550cc8ae86Sav 	if ((error = mc_poll_init()) != 0) {
3560cc8ae86Sav 		ddi_soft_state_fini(&mc_statep);
3570cc8ae86Sav 		return (error);
3580cc8ae86Sav 	}
3590cc8ae86Sav 
36025cf1a30Sjl 	mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
36125cf1a30Sjl 	if (&opl_get_mem_unum)
36225cf1a30Sjl 		opl_get_mem_unum = mc_get_mem_unum;
3630cc8ae86Sav 	if (&opl_get_mem_sid)
3640cc8ae86Sav 		opl_get_mem_sid = mc_get_mem_sid;
3650cc8ae86Sav 	if (&opl_get_mem_offset)
3660cc8ae86Sav 		opl_get_mem_offset = mc_get_mem_offset;
3670cc8ae86Sav 	if (&opl_get_mem_addr)
3680cc8ae86Sav 		opl_get_mem_addr = mc_get_mem_addr;
3690cc8ae86Sav 
3700cc8ae86Sav 	node = prom_rootnode();
3710cc8ae86Sav 	plen = prom_getproplen(node, "model");
3720cc8ae86Sav 
3730cc8ae86Sav 	if (plen > 0 && plen < sizeof (model)) {
3740cc8ae86Sav 		(void) prom_getprop(node, "model", model);
3750cc8ae86Sav 		model[plen] = '\0';
3760cc8ae86Sav 		if (strcmp(model, "FF1") == 0)
3770cc8ae86Sav 			plat_model = MODEL_FF1;
3780cc8ae86Sav 		else if (strcmp(model, "FF2") == 0)
3790cc8ae86Sav 			plat_model = MODEL_FF2;
3800cc8ae86Sav 		else if (strncmp(model, "DC", 2) == 0)
3810cc8ae86Sav 			plat_model = MODEL_DC;
3820cc8ae86Sav 	}
38325cf1a30Sjl 
38425cf1a30Sjl 	error =  mod_install(&modlinkage);
38525cf1a30Sjl 	if (error != 0) {
38625cf1a30Sjl 		if (&opl_get_mem_unum)
38725cf1a30Sjl 			opl_get_mem_unum = NULL;
3880cc8ae86Sav 		if (&opl_get_mem_sid)
3890cc8ae86Sav 			opl_get_mem_sid = NULL;
3900cc8ae86Sav 		if (&opl_get_mem_offset)
3910cc8ae86Sav 			opl_get_mem_offset = NULL;
3920cc8ae86Sav 		if (&opl_get_mem_addr)
3930cc8ae86Sav 			opl_get_mem_addr = NULL;
39425cf1a30Sjl 		mutex_destroy(&mcmutex);
3950cc8ae86Sav 		mc_poll_fini();
39625cf1a30Sjl 		ddi_soft_state_fini(&mc_statep);
39725cf1a30Sjl 	}
39825cf1a30Sjl 	return (error);
39925cf1a30Sjl }
40025cf1a30Sjl 
40125cf1a30Sjl int
40225cf1a30Sjl _fini(void)
40325cf1a30Sjl {
40425cf1a30Sjl 	int error;
40525cf1a30Sjl 
40625cf1a30Sjl 	if ((error = mod_remove(&modlinkage)) != 0)
40725cf1a30Sjl 		return (error);
40825cf1a30Sjl 
40925cf1a30Sjl 	if (&opl_get_mem_unum)
41025cf1a30Sjl 		opl_get_mem_unum = NULL;
4110cc8ae86Sav 	if (&opl_get_mem_sid)
4120cc8ae86Sav 		opl_get_mem_sid = NULL;
4130cc8ae86Sav 	if (&opl_get_mem_offset)
4140cc8ae86Sav 		opl_get_mem_offset = NULL;
4150cc8ae86Sav 	if (&opl_get_mem_addr)
4160cc8ae86Sav 		opl_get_mem_addr = NULL;
41725cf1a30Sjl 
4180cc8ae86Sav 	mutex_destroy(&mcmutex);
4190cc8ae86Sav 	mc_poll_fini();
42025cf1a30Sjl 	ddi_soft_state_fini(&mc_statep);
42125cf1a30Sjl 
42225cf1a30Sjl 	return (0);
42325cf1a30Sjl }
42425cf1a30Sjl 
42525cf1a30Sjl int
42625cf1a30Sjl _info(struct modinfo *modinfop)
42725cf1a30Sjl {
42825cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
42925cf1a30Sjl }
43025cf1a30Sjl 
4310cc8ae86Sav static void
4320cc8ae86Sav mc_polling_thread()
4330cc8ae86Sav {
4340cc8ae86Sav 	mutex_enter(&mc_polling_lock);
4350cc8ae86Sav 	mc_pollthr_running = 1;
4360cc8ae86Sav 	while (!(mc_poll_cmd & MC_POLL_EXIT)) {
4370cc8ae86Sav 		mc_polling();
4380cc8ae86Sav 		cv_timedwait(&mc_polling_cv, &mc_polling_lock,
4390cc8ae86Sav 		    ddi_get_lbolt() + mc_timeout_period);
4400cc8ae86Sav 	}
4410cc8ae86Sav 	mc_pollthr_running = 0;
4420cc8ae86Sav 
4430cc8ae86Sav 	/*
4440cc8ae86Sav 	 * signal if any one is waiting for this thread to exit.
4450cc8ae86Sav 	 */
4460cc8ae86Sav 	cv_signal(&mc_poll_exit_cv);
4470cc8ae86Sav 	mutex_exit(&mc_polling_lock);
4480cc8ae86Sav 	thread_exit();
4490cc8ae86Sav 	/* NOTREACHED */
4500cc8ae86Sav }
4510cc8ae86Sav 
4520cc8ae86Sav static int
4530cc8ae86Sav mc_poll_init()
4540cc8ae86Sav {
4550cc8ae86Sav 	mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL);
4560cc8ae86Sav 	cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL);
4570cc8ae86Sav 	cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL);
4580cc8ae86Sav 	return (0);
4590cc8ae86Sav }
4600cc8ae86Sav 
4610cc8ae86Sav static void
4620cc8ae86Sav mc_poll_fini()
4630cc8ae86Sav {
4640cc8ae86Sav 	mutex_enter(&mc_polling_lock);
4650cc8ae86Sav 	if (mc_pollthr_running) {
4660cc8ae86Sav 		mc_poll_cmd = MC_POLL_EXIT;
4670cc8ae86Sav 		cv_signal(&mc_polling_cv);
4680cc8ae86Sav 		while (mc_pollthr_running) {
4690cc8ae86Sav 			cv_wait(&mc_poll_exit_cv, &mc_polling_lock);
4700cc8ae86Sav 		}
4710cc8ae86Sav 	}
4720cc8ae86Sav 	mutex_exit(&mc_polling_lock);
4730cc8ae86Sav 	mutex_destroy(&mc_polling_lock);
4740cc8ae86Sav 	cv_destroy(&mc_polling_cv);
4750cc8ae86Sav 	cv_destroy(&mc_poll_exit_cv);
4760cc8ae86Sav }
4770cc8ae86Sav 
47825cf1a30Sjl static int
47925cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
48025cf1a30Sjl {
48125cf1a30Sjl 	mc_opl_t *mcp;
48225cf1a30Sjl 	int instance;
4830cc8ae86Sav 	int rv;
48425cf1a30Sjl 
48525cf1a30Sjl 	/* get the instance of this devi */
48625cf1a30Sjl 	instance = ddi_get_instance(devi);
48725cf1a30Sjl 
48825cf1a30Sjl 	switch (cmd) {
48925cf1a30Sjl 	case DDI_ATTACH:
49025cf1a30Sjl 		break;
49125cf1a30Sjl 	case DDI_RESUME:
49225cf1a30Sjl 		mcp = ddi_get_soft_state(mc_statep, instance);
4930cc8ae86Sav 		rv = mc_resume(mcp, MC_DRIVER_SUSPENDED);
4940cc8ae86Sav 		return (rv);
49525cf1a30Sjl 	default:
49625cf1a30Sjl 		return (DDI_FAILURE);
49725cf1a30Sjl 	}
49825cf1a30Sjl 
49925cf1a30Sjl 	if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS)
50025cf1a30Sjl 		return (DDI_FAILURE);
50125cf1a30Sjl 
50225cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
50325cf1a30Sjl 		goto bad;
50425cf1a30Sjl 	}
50525cf1a30Sjl 
5060cc8ae86Sav 	if (mc_timeout_period == 0) {
5070cc8ae86Sav 		mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi,
5080cc8ae86Sav 			DDI_PROP_DONTPASS, "mc-timeout-interval-sec",
5090cc8ae86Sav 			mc_patrol_interval_sec);
5100cc8ae86Sav 		mc_timeout_period = drv_usectohz(
5110cc8ae86Sav 			1000000 * mc_patrol_interval_sec / OPL_MAX_BOARDS);
5120cc8ae86Sav 	}
5130cc8ae86Sav 
51425cf1a30Sjl 	/* set informations in mc state */
51525cf1a30Sjl 	mcp->mc_dip = devi;
51625cf1a30Sjl 
51725cf1a30Sjl 	if (mc_board_add(mcp))
51825cf1a30Sjl 		goto bad;
51925cf1a30Sjl 
52025cf1a30Sjl 	insert_mcp(mcp);
5210cc8ae86Sav 
5220cc8ae86Sav 	/*
5230cc8ae86Sav 	 * Start the polling thread if it is not running already.
5240cc8ae86Sav 	 */
5250cc8ae86Sav 	mutex_enter(&mc_polling_lock);
5260cc8ae86Sav 	if (!mc_pollthr_running) {
5270cc8ae86Sav 		(void) thread_create(NULL, 0, (void (*)())mc_polling_thread,
5280cc8ae86Sav 			NULL, 0, &p0, TS_RUN, mc_poll_priority);
5290cc8ae86Sav 	}
5300cc8ae86Sav 	mutex_exit(&mc_polling_lock);
53125cf1a30Sjl 	ddi_report_dev(devi);
53225cf1a30Sjl 
53325cf1a30Sjl 	return (DDI_SUCCESS);
53425cf1a30Sjl 
53525cf1a30Sjl bad:
53625cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
53725cf1a30Sjl 	return (DDI_FAILURE);
53825cf1a30Sjl }
53925cf1a30Sjl 
54025cf1a30Sjl /* ARGSUSED */
54125cf1a30Sjl static int
54225cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
54325cf1a30Sjl {
5440cc8ae86Sav 	int rv;
54525cf1a30Sjl 	int instance;
54625cf1a30Sjl 	mc_opl_t *mcp;
54725cf1a30Sjl 
54825cf1a30Sjl 	/* get the instance of this devi */
54925cf1a30Sjl 	instance = ddi_get_instance(devi);
55025cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
55125cf1a30Sjl 		return (DDI_FAILURE);
55225cf1a30Sjl 	}
55325cf1a30Sjl 
55425cf1a30Sjl 	switch (cmd) {
55525cf1a30Sjl 	case DDI_SUSPEND:
5560cc8ae86Sav 		rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED);
5570cc8ae86Sav 		return (rv);
55825cf1a30Sjl 	case DDI_DETACH:
55925cf1a30Sjl 		break;
56025cf1a30Sjl 	default:
56125cf1a30Sjl 		return (DDI_FAILURE);
56225cf1a30Sjl 	}
56325cf1a30Sjl 
5640cc8ae86Sav 	delete_mcp(mcp);
56525cf1a30Sjl 	if (mc_board_del(mcp) != DDI_SUCCESS) {
56625cf1a30Sjl 		return (DDI_FAILURE);
56725cf1a30Sjl 	}
56825cf1a30Sjl 
56925cf1a30Sjl 	/* free up the soft state */
57025cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
57125cf1a30Sjl 
57225cf1a30Sjl 	return (DDI_SUCCESS);
57325cf1a30Sjl }
57425cf1a30Sjl 
57525cf1a30Sjl /* ARGSUSED */
57625cf1a30Sjl static int
57725cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
57825cf1a30Sjl {
57925cf1a30Sjl 	return (0);
58025cf1a30Sjl }
58125cf1a30Sjl 
58225cf1a30Sjl /* ARGSUSED */
58325cf1a30Sjl static int
58425cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
58525cf1a30Sjl {
58625cf1a30Sjl 	return (0);
58725cf1a30Sjl }
58825cf1a30Sjl 
58925cf1a30Sjl /* ARGSUSED */
59025cf1a30Sjl static int
59125cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
59225cf1a30Sjl 	int *rvalp)
59325cf1a30Sjl {
5940cc8ae86Sav #ifdef DEBUG
5950cc8ae86Sav 	return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp));
5960cc8ae86Sav #else
59725cf1a30Sjl 	return (ENXIO);
5980cc8ae86Sav #endif
59925cf1a30Sjl }
60025cf1a30Sjl 
60125cf1a30Sjl /*
60225cf1a30Sjl  * PA validity check:
603738dd194Shyw  * This function return 1 if the PA is a valid PA
604738dd194Shyw  * in the running Solaris instance i.e. in physinstall
605738dd194Shyw  * Otherwise, return 0.
60625cf1a30Sjl  */
60725cf1a30Sjl 
60825cf1a30Sjl /* ARGSUSED */
60925cf1a30Sjl static int
61025cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr)
61125cf1a30Sjl {
61225cf1a30Sjl 	if (mcp->mlist == NULL)
61325cf1a30Sjl 		mc_get_mlist(mcp);
61425cf1a30Sjl 
61525cf1a30Sjl 	if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) {
61625cf1a30Sjl 		return (1);
61725cf1a30Sjl 	}
61825cf1a30Sjl 	return (0);
61925cf1a30Sjl }
62025cf1a30Sjl 
62125cf1a30Sjl /*
62225cf1a30Sjl  * mac-pa translation routines.
62325cf1a30Sjl  *
62425cf1a30Sjl  *    Input: mc driver state, (LSB#, Bank#, DIMM address)
62525cf1a30Sjl  *    Output: physical address
62625cf1a30Sjl  *
62725cf1a30Sjl  *    Valid   - return value:  0
62825cf1a30Sjl  *    Invalid - return value: -1
62925cf1a30Sjl  */
63025cf1a30Sjl static int
63125cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa)
63225cf1a30Sjl {
63325cf1a30Sjl 	int i;
63425cf1a30Sjl 	uint64_t pa_offset = 0;
63525cf1a30Sjl 	int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1;
63625cf1a30Sjl 	int bank = maddr->ma_bank;
63725cf1a30Sjl 	mc_addr_t maddr1;
63825cf1a30Sjl 	int bank0, bank1;
63925cf1a30Sjl 
64025cf1a30Sjl 	MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
64125cf1a30Sjl 		maddr->ma_dimm_addr);
64225cf1a30Sjl 
64325cf1a30Sjl 	/* loc validity check */
64425cf1a30Sjl 	ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd);
64525cf1a30Sjl 	ASSERT(bank >= 0 && OPL_BANK_MAX > bank);
64625cf1a30Sjl 
64725cf1a30Sjl 	/* Do translation */
64825cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
64925cf1a30Sjl 		int pa_bit = 0;
65025cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
65125cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
65225cf1a30Sjl 			pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1;
65325cf1a30Sjl 		} else if (mc_bit == MP_NONE) {
65425cf1a30Sjl 			pa_bit = 0;
65525cf1a30Sjl 		} else if (mc_bit == MP_BANK_0) {
65625cf1a30Sjl 			pa_bit = bank & 1;
65725cf1a30Sjl 		} else if (mc_bit == MP_BANK_1) {
65825cf1a30Sjl 			pa_bit = (bank >> 1) & 1;
65925cf1a30Sjl 		} else if (mc_bit == MP_BANK_2) {
66025cf1a30Sjl 			pa_bit = (bank >> 2) & 1;
66125cf1a30Sjl 		}
66225cf1a30Sjl 		pa_offset |= ((uint64_t)pa_bit) << i;
66325cf1a30Sjl 	}
66425cf1a30Sjl 	*pa = mcp->mc_start_address + pa_offset;
66525cf1a30Sjl 	MC_LOG("pa = %lx\n", *pa);
66625cf1a30Sjl 
66725cf1a30Sjl 	if (pa_to_maddr(mcp, *pa, &maddr1) == -1) {
6680cc8ae86Sav 		cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to "
6690cc8ae86Sav 		    "convert PA %lx\n", maddr->ma_bd, bank,
6700cc8ae86Sav 		    maddr->ma_dimm_addr, *pa);
67125cf1a30Sjl 		return (-1);
67225cf1a30Sjl 	}
67325cf1a30Sjl 
6740cc8ae86Sav 	/*
6750cc8ae86Sav 	 * In mirror mode, PA is always translated to the even bank.
6760cc8ae86Sav 	 */
67725cf1a30Sjl 	if (IS_MIRROR(mcp, maddr->ma_bank)) {
67825cf1a30Sjl 		bank0 = maddr->ma_bank & ~(1);
67925cf1a30Sjl 		bank1 = maddr1.ma_bank & ~(1);
68025cf1a30Sjl 	} else {
68125cf1a30Sjl 		bank0 = maddr->ma_bank;
68225cf1a30Sjl 		bank1 = maddr1.ma_bank;
68325cf1a30Sjl 	}
68425cf1a30Sjl 	/*
68525cf1a30Sjl 	 * there is no need to check ma_bd because it is generated from
68625cf1a30Sjl 	 * mcp.  They are the same.
68725cf1a30Sjl 	 */
68825cf1a30Sjl 	if ((bank0 == bank1) &&
68925cf1a30Sjl 		(maddr->ma_dimm_addr == maddr1.ma_dimm_addr)) {
69025cf1a30Sjl 		return (0);
69125cf1a30Sjl 	} else {
69225cf1a30Sjl 		cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, "
69325cf1a30Sjl 			"PA %lx, target /LSB%d/B%d/%x\n",
69425cf1a30Sjl 			maddr->ma_bd, bank, maddr->ma_dimm_addr,
69525cf1a30Sjl 			*pa, maddr1.ma_bd, maddr1.ma_bank,
69625cf1a30Sjl 			maddr1.ma_dimm_addr);
69725cf1a30Sjl 		return (-1);
69825cf1a30Sjl 	}
69925cf1a30Sjl }
70025cf1a30Sjl 
70125cf1a30Sjl /*
70225cf1a30Sjl  * PA to CS (used by pa_to_maddr).
70325cf1a30Sjl  */
70425cf1a30Sjl static int
70525cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset)
70625cf1a30Sjl {
70725cf1a30Sjl 	int i;
708738dd194Shyw 	int cs = 1;
70925cf1a30Sjl 
71025cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
71125cf1a30Sjl 		/* MAC address bit<29> is arranged on the same PA bit */
71225cf1a30Sjl 		/* on both table. So we may use any table. */
71325cf1a30Sjl 		if (mcp->mc_trans_table[0][i] == CS_SHIFT) {
71425cf1a30Sjl 			cs = (pa_offset >> i) & 1;
71525cf1a30Sjl 			break;
71625cf1a30Sjl 		}
71725cf1a30Sjl 	}
71825cf1a30Sjl 	return (cs);
71925cf1a30Sjl }
72025cf1a30Sjl 
72125cf1a30Sjl /*
72225cf1a30Sjl  * PA to DIMM (used by pa_to_maddr).
72325cf1a30Sjl  */
72425cf1a30Sjl /* ARGSUSED */
72525cf1a30Sjl static uint32_t
72625cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset)
72725cf1a30Sjl {
72825cf1a30Sjl 	int i;
72925cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
73025cf1a30Sjl 	uint32_t dimm_addr = 0;
73125cf1a30Sjl 
73225cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
73325cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
73425cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
73525cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
73625cf1a30Sjl 			dimm_addr |= pa_bit_value << mc_bit;
73725cf1a30Sjl 		}
73825cf1a30Sjl 	}
739738dd194Shyw 	dimm_addr |= cs << CS_SHIFT;
74025cf1a30Sjl 	return (dimm_addr);
74125cf1a30Sjl }
74225cf1a30Sjl 
74325cf1a30Sjl /*
74425cf1a30Sjl  * PA to Bank (used by pa_to_maddr).
74525cf1a30Sjl  */
74625cf1a30Sjl static int
74725cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset)
74825cf1a30Sjl {
74925cf1a30Sjl 	int i;
75025cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
75125cf1a30Sjl 	int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT];
75225cf1a30Sjl 
75325cf1a30Sjl 
75425cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
75525cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
75625cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
75725cf1a30Sjl 		switch (mc_bit) {
75825cf1a30Sjl 		case MP_BANK_0:
75925cf1a30Sjl 			bankno |= pa_bit_value;
76025cf1a30Sjl 			break;
76125cf1a30Sjl 		case MP_BANK_1:
76225cf1a30Sjl 			bankno |= pa_bit_value << 1;
76325cf1a30Sjl 			break;
76425cf1a30Sjl 		case MP_BANK_2:
76525cf1a30Sjl 			bankno |= pa_bit_value << 2;
76625cf1a30Sjl 			break;
76725cf1a30Sjl 		}
76825cf1a30Sjl 	}
76925cf1a30Sjl 
77025cf1a30Sjl 	return (bankno);
77125cf1a30Sjl }
77225cf1a30Sjl 
77325cf1a30Sjl /*
77425cf1a30Sjl  * PA to MAC address translation
77525cf1a30Sjl  *
77625cf1a30Sjl  *   Input: MAC driver state, physicall adress
77725cf1a30Sjl  *   Output: LSB#, Bank id, mac address
77825cf1a30Sjl  *
77925cf1a30Sjl  *    Valid   - return value:  0
78025cf1a30Sjl  *    Invalid - return value: -1
78125cf1a30Sjl  */
78225cf1a30Sjl 
78325cf1a30Sjl int
78425cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr)
78525cf1a30Sjl {
78625cf1a30Sjl 	uint64_t pa_offset;
78725cf1a30Sjl 
788738dd194Shyw 	if (!mc_rangecheck_pa(mcp, pa))
78925cf1a30Sjl 		return (-1);
79025cf1a30Sjl 
79125cf1a30Sjl 	/* Do translation */
79225cf1a30Sjl 	pa_offset = pa - mcp->mc_start_address;
79325cf1a30Sjl 
79425cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
795*aeb241b2Sav 	maddr->ma_phys_bd = mcp->mc_phys_board_num;
79625cf1a30Sjl 	maddr->ma_bank = pa_to_bank(mcp, pa_offset);
79725cf1a30Sjl 	maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset);
79825cf1a30Sjl 	MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n",
79925cf1a30Sjl 		pa_offset, maddr->ma_bd, maddr->ma_bank, maddr->ma_dimm_addr);
80025cf1a30Sjl 	return (0);
80125cf1a30Sjl }
80225cf1a30Sjl 
8030cc8ae86Sav /*
8040cc8ae86Sav  * UNUM format for DC is "/CMUnn/MEMxyZ", where
8050cc8ae86Sav  *	nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3.
8060cc8ae86Sav  *	x = MAC 0..3
8070cc8ae86Sav  *	y = 0..3 (slot info).
8080cc8ae86Sav  *	Z = 'A' or 'B'
8090cc8ae86Sav  *
8100cc8ae86Sav  * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where
8110cc8ae86Sav  *	x = 0..3 (MEMB number)
8120cc8ae86Sav  *	y = 0..3 (slot info).
8130cc8ae86Sav  *	Z = 'A' or 'B'
8140cc8ae86Sav  *
8150cc8ae86Sav  * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ"
8160cc8ae86Sav  *	x = 0..7 (MEMB number)
8170cc8ae86Sav  *	y = 0..3 (slot info).
8180cc8ae86Sav  *	Z = 'A' or 'B'
8190cc8ae86Sav  */
8200cc8ae86Sav int
821*aeb241b2Sav mc_set_mem_unum(char *buf, int buflen, int sb, int bank,
8220cc8ae86Sav     uint32_t mf_type, uint32_t d_slot)
8230cc8ae86Sav {
8240cc8ae86Sav 	char *dimmnm;
8250cc8ae86Sav 	char memb_num;
826*aeb241b2Sav 	int cs;
8270cc8ae86Sav 	int i;
828*aeb241b2Sav 	int j;
8290cc8ae86Sav 
830*aeb241b2Sav 	cs = SLOT_TO_CS(d_slot);
8310cc8ae86Sav 
8320cc8ae86Sav 	if (plat_model == MODEL_DC) {
8330cc8ae86Sav 		if (mf_type == FLT_TYPE_PERMANENT_CE) {
8340cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
8350cc8ae86Sav 			dimmnm = mc_dc_dimm_unum_table[i];
8360cc8ae86Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s",
8370cc8ae86Sav 			    model_names[plat_model].unit_name, sb, dimmnm);
8380cc8ae86Sav 		} else {
8390cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
840*aeb241b2Sav 			j = (cs == 0) ?  i : i + 2;
841*aeb241b2Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
8420cc8ae86Sav 			    model_names[plat_model].unit_name, sb,
843*aeb241b2Sav 			    mc_dc_dimm_unum_table[j],
844*aeb241b2Sav 			    mc_dc_dimm_unum_table[j + 1]);
8450cc8ae86Sav 		}
8460cc8ae86Sav 	} else {
8470cc8ae86Sav 		if (mf_type == FLT_TYPE_PERMANENT_CE) {
848*aeb241b2Sav 			i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
8490cc8ae86Sav 			dimmnm = mc_ff_dimm_unum_table[i];
8500cc8ae86Sav 			memb_num = dimmnm[0];
8510cc8ae86Sav 			snprintf(buf, buflen, "/%s/%s%c/MEM%s",
8520cc8ae86Sav 			    model_names[plat_model].unit_name,
8530cc8ae86Sav 			    model_names[plat_model].mem_name,
8540cc8ae86Sav 			    memb_num, &dimmnm[1]);
8550cc8ae86Sav 		} else {
8560cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
857*aeb241b2Sav 			j = (cs == 0) ?  i : i + 2;
8580cc8ae86Sav 			memb_num = mc_ff_dimm_unum_table[i][0],
8590cc8ae86Sav 			snprintf(buf, buflen,
860*aeb241b2Sav 			    "/%s/%s%c/MEM%s MEM%s",
8610cc8ae86Sav 			    model_names[plat_model].unit_name,
8620cc8ae86Sav 			    model_names[plat_model].mem_name, memb_num,
863*aeb241b2Sav 			    &mc_ff_dimm_unum_table[j][1],
864*aeb241b2Sav 			    &mc_ff_dimm_unum_table[j + 1][1]);
8650cc8ae86Sav 		}
8660cc8ae86Sav 	}
8670cc8ae86Sav 	return (0);
8680cc8ae86Sav }
8690cc8ae86Sav 
87025cf1a30Sjl static void
87125cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt)
87225cf1a30Sjl {
87325cf1a30Sjl 	char buf[FM_MAX_CLASS];
87425cf1a30Sjl 	char device_path[MAXPATHLEN];
8750cc8ae86Sav 	char sid[MAXPATHLEN];
87625cf1a30Sjl 	nv_alloc_t *nva = NULL;
87725cf1a30Sjl 	nvlist_t *ereport, *detector, *resource;
87825cf1a30Sjl 	errorq_elem_t *eqep;
87925cf1a30Sjl 	int nflts;
88025cf1a30Sjl 	mc_flt_stat_t *flt_stat;
8810cc8ae86Sav 	int i, n;
8820cc8ae86Sav 	int blen = MAXPATHLEN;
8830cc8ae86Sav 	char *p, *s = NULL;
88425cf1a30Sjl 	uint32_t values[2], synd[2], dslot[2];
8850cc8ae86Sav 	uint64_t offset = (uint64_t)-1;
8860cc8ae86Sav 	int ret = -1;
88725cf1a30Sjl 
88825cf1a30Sjl 	if (panicstr) {
88925cf1a30Sjl 		eqep = errorq_reserve(ereport_errorq);
89025cf1a30Sjl 		if (eqep == NULL)
89125cf1a30Sjl 			return;
89225cf1a30Sjl 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
89325cf1a30Sjl 		nva = errorq_elem_nva(ereport_errorq, eqep);
89425cf1a30Sjl 	} else {
89525cf1a30Sjl 		ereport = fm_nvlist_create(nva);
89625cf1a30Sjl 	}
89725cf1a30Sjl 
89825cf1a30Sjl 	/*
89925cf1a30Sjl 	 * Create the scheme "dev" FMRI.
90025cf1a30Sjl 	 */
90125cf1a30Sjl 	detector = fm_nvlist_create(nva);
90225cf1a30Sjl 	resource = fm_nvlist_create(nva);
90325cf1a30Sjl 
90425cf1a30Sjl 	nflts = mc_aflt->mflt_nflts;
90525cf1a30Sjl 
90625cf1a30Sjl 	ASSERT(nflts >= 1 && nflts <= 2);
90725cf1a30Sjl 
90825cf1a30Sjl 	flt_stat = mc_aflt->mflt_stat[0];
90925cf1a30Sjl 	(void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path);
91025cf1a30Sjl 	(void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
91125cf1a30Sjl 	    device_path, NULL);
91225cf1a30Sjl 
91325cf1a30Sjl 	/*
91425cf1a30Sjl 	 * Encode all the common data into the ereport.
91525cf1a30Sjl 	 */
91625cf1a30Sjl 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s",
91725cf1a30Sjl 		MC_OPL_ERROR_CLASS,
91825cf1a30Sjl 		mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS :
91925cf1a30Sjl 		MC_OPL_MI_SUBCLASS,
92025cf1a30Sjl 		mc_aflt->mflt_erpt_class);
92125cf1a30Sjl 
92225cf1a30Sjl 	MC_LOG("mc_ereport_post: ereport %s\n", buf);
92325cf1a30Sjl 
92425cf1a30Sjl 
92525cf1a30Sjl 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
92625cf1a30Sjl 		fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1),
92725cf1a30Sjl 		detector, NULL);
92825cf1a30Sjl 
92925cf1a30Sjl 	/*
93025cf1a30Sjl 	 * Set payload.
93125cf1a30Sjl 	 */
93225cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32,
93325cf1a30Sjl 		flt_stat->mf_flt_maddr.ma_bd, NULL);
93425cf1a30Sjl 
93525cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64,
93625cf1a30Sjl 		flt_stat->mf_flt_paddr, NULL);
93725cf1a30Sjl 
93825cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
93925cf1a30Sjl 		fm_payload_set(ereport, MC_OPL_FLT_TYPE,
94025cf1a30Sjl 			DATA_TYPE_UINT8, ECC_STICKY, NULL);
94125cf1a30Sjl 	}
94225cf1a30Sjl 
94325cf1a30Sjl 	for (i = 0; i < nflts; i++)
94425cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank;
94525cf1a30Sjl 
94625cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY,
94725cf1a30Sjl 		nflts, values, NULL);
94825cf1a30Sjl 
94925cf1a30Sjl 	for (i = 0; i < nflts; i++)
95025cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_cntl;
95125cf1a30Sjl 
95225cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY,
95325cf1a30Sjl 		nflts, values, NULL);
95425cf1a30Sjl 
95525cf1a30Sjl 	for (i = 0; i < nflts; i++)
95625cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_add;
95725cf1a30Sjl 
9580cc8ae86Sav 	/* offset is set only for PCE */
9590cc8ae86Sav 	if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) {
9600cc8ae86Sav 		offset = values[0];
9610cc8ae86Sav 
9620cc8ae86Sav 	}
96325cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY,
96425cf1a30Sjl 		nflts, values, NULL);
96525cf1a30Sjl 
96625cf1a30Sjl 	for (i = 0; i < nflts; i++)
96725cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_log;
96825cf1a30Sjl 
96925cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY,
97025cf1a30Sjl 		nflts, values, NULL);
97125cf1a30Sjl 
97225cf1a30Sjl 	for (i = 0; i < nflts; i++) {
97325cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
97425cf1a30Sjl 		if (flt_stat->mf_errlog_valid) {
97525cf1a30Sjl 			synd[i] = flt_stat->mf_synd;
97625cf1a30Sjl 			dslot[i] = flt_stat->mf_dimm_slot;
97725cf1a30Sjl 			values[i] = flt_stat->mf_dram_place;
97825cf1a30Sjl 		} else {
97925cf1a30Sjl 			synd[i] = 0;
98025cf1a30Sjl 			dslot[i] = 0;
98125cf1a30Sjl 			values[i] = 0;
98225cf1a30Sjl 		}
98325cf1a30Sjl 	}
98425cf1a30Sjl 
98525cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_SYND,
98625cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, synd, NULL);
98725cf1a30Sjl 
98825cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT,
98925cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, dslot, NULL);
99025cf1a30Sjl 
99125cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_DRAM,
99225cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, values, NULL);
99325cf1a30Sjl 
99425cf1a30Sjl 	device_path[0] = 0;
99525cf1a30Sjl 	p = &device_path[0];
9960cc8ae86Sav 	sid[0] = 0;
9970cc8ae86Sav 	s = &sid[0];
9980cc8ae86Sav 	ret = 0;
99925cf1a30Sjl 
100025cf1a30Sjl 	for (i = 0; i < nflts; i++) {
10010cc8ae86Sav 		int bank;
100225cf1a30Sjl 
100325cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
10040cc8ae86Sav 		bank = flt_stat->mf_flt_maddr.ma_bank;
10050cc8ae86Sav 		ret =  mc_set_mem_unum(p + strlen(p), blen,
1006*aeb241b2Sav 			flt_stat->mf_flt_maddr.ma_phys_bd, bank,
1007*aeb241b2Sav 			flt_stat->mf_type, flt_stat->mf_dimm_slot);
10080cc8ae86Sav 
10090cc8ae86Sav 		if (ret != 0) {
10100cc8ae86Sav 			cmn_err(CE_WARN,
10110cc8ae86Sav 			    "mc_ereport_post: Failed to determine the unum "
10120cc8ae86Sav 			    "for board=%d bank=%d type=0x%x slot=0x%x",
10130cc8ae86Sav 			    flt_stat->mf_flt_maddr.ma_bd, bank,
10140cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10150cc8ae86Sav 			continue;
101625cf1a30Sjl 		}
10170cc8ae86Sav 		n = strlen(device_path);
101825cf1a30Sjl 		blen = MAXPATHLEN - n;
101925cf1a30Sjl 		p = &device_path[n];
102025cf1a30Sjl 		if (i < (nflts - 1)) {
102125cf1a30Sjl 			snprintf(p, blen, " ");
10220cc8ae86Sav 			blen--;
10230cc8ae86Sav 			p++;
10240cc8ae86Sav 		}
10250cc8ae86Sav 
10260cc8ae86Sav 		if (ret == 0) {
10270cc8ae86Sav 			ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s),
1028*aeb241b2Sav 			    blen, flt_stat->mf_flt_maddr.ma_phys_bd, bank,
10290cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10300cc8ae86Sav 
103125cf1a30Sjl 		}
103225cf1a30Sjl 	}
103325cf1a30Sjl 
103425cf1a30Sjl 	(void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION,
10350cc8ae86Sav 		NULL, device_path, (ret == 0) ? sid : NULL,
10360cc8ae86Sav 		(ret == 0) ? offset : (uint64_t)-1);
103725cf1a30Sjl 
103825cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST,
103925cf1a30Sjl 		resource, NULL);
104025cf1a30Sjl 
104125cf1a30Sjl 	if (panicstr) {
104225cf1a30Sjl 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
104325cf1a30Sjl 	} else {
104425cf1a30Sjl 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
104525cf1a30Sjl 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
104625cf1a30Sjl 		fm_nvlist_destroy(detector, FM_NVA_FREE);
104725cf1a30Sjl 		fm_nvlist_destroy(resource, FM_NVA_FREE);
104825cf1a30Sjl 	}
104925cf1a30Sjl }
105025cf1a30Sjl 
10510cc8ae86Sav 
105225cf1a30Sjl static void
105325cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt)
105425cf1a30Sjl {
105525cf1a30Sjl 	int rv;
105625cf1a30Sjl 	uint64_t pa = (uint64_t)(-1);
10570cc8ae86Sav 	int i;
105825cf1a30Sjl 
105925cf1a30Sjl 	MC_LOG("mc_err_drain: %s\n",
106025cf1a30Sjl 		mc_aflt->mflt_erpt_class);
106125cf1a30Sjl 	/*
106225cf1a30Sjl 	 * we come here only when we have:
106325cf1a30Sjl 	 * In mirror mode: CMPE, MUE, SUE
106425cf1a30Sjl 	 * In normal mode: UE, Permanent CE
106525cf1a30Sjl 	 */
10660cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
10670cc8ae86Sav 		rv = mcaddr_to_pa(mc_aflt->mflt_mcp,
10680cc8ae86Sav 			&(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa);
1069738dd194Shyw 
1070738dd194Shyw 		/* Ensure the pa is valid (not in isolated memory block) */
1071738dd194Shyw 		if (rv == 0 && pa_is_valid(mc_aflt->mflt_mcp, pa))
10720cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = pa;
10730cc8ae86Sav 		else
10740cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1;
10750cc8ae86Sav 	}
10760cc8ae86Sav 
1077738dd194Shyw 	MC_LOG("mc_err_drain:pa = %lx\n", pa);
107825cf1a30Sjl 
1079738dd194Shyw 	switch (page_retire_check(pa, NULL)) {
1080738dd194Shyw 	case 0:
1081738dd194Shyw 	case EAGAIN:
1082738dd194Shyw 		MC_LOG("Page retired or pending\n");
1083738dd194Shyw 		return;
1084738dd194Shyw 	case EIO:
1085738dd194Shyw 		/*
1086738dd194Shyw 		 * Do page retirement except for the PCE case.
1087738dd194Shyw 		 * This is taken care by the OPL DE
1088738dd194Shyw 		 */
1089738dd194Shyw 		if (mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) {
1090738dd194Shyw 			MC_LOG("offline page at pa %lx error %x\n", pa,
1091738dd194Shyw 				mc_aflt->mflt_pr);
1092738dd194Shyw 			(void) page_retire(pa, mc_aflt->mflt_pr);
109325cf1a30Sjl 		}
1094738dd194Shyw 		break;
1095738dd194Shyw 	case EINVAL:
1096738dd194Shyw 	default:
1097738dd194Shyw 		/*
1098738dd194Shyw 		 * Some memory do not have page structure so
1099738dd194Shyw 		 * we keep going in case of EINVAL.
1100738dd194Shyw 		 */
1101738dd194Shyw 		break;
110225cf1a30Sjl 	}
110325cf1a30Sjl 
11040cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
11050cc8ae86Sav 		mc_aflt_t mc_aflt0;
11060cc8ae86Sav 		if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) {
11070cc8ae86Sav 			mc_aflt0 = *mc_aflt;
11080cc8ae86Sav 			mc_aflt0.mflt_nflts = 1;
11090cc8ae86Sav 			mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i];
11100cc8ae86Sav 			mc_ereport_post(&mc_aflt0);
11110cc8ae86Sav 		}
11120cc8ae86Sav 	}
11130cc8ae86Sav }
111425cf1a30Sjl 
111525cf1a30Sjl /*
111625cf1a30Sjl  * The restart address is actually defined in unit of PA[37:6]
111725cf1a30Sjl  * the mac patrol will convert that to dimm offset.  If the
111825cf1a30Sjl  * address is not in the bank, it will continue to search for
111925cf1a30Sjl  * the next PA that is within the bank.
112025cf1a30Sjl  *
112125cf1a30Sjl  * Also the mac patrol scans the dimms based on PA, not
112225cf1a30Sjl  * dimm offset.
112325cf1a30Sjl  */
112425cf1a30Sjl static int
1125738dd194Shyw restart_patrol(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr_info)
112625cf1a30Sjl {
112725cf1a30Sjl 	uint64_t pa;
112825cf1a30Sjl 	int rv;
112925cf1a30Sjl 
1130738dd194Shyw 	if (rsaddr_info == NULL || (rsaddr_info->mi_valid == 0)) {
113125cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
113225cf1a30Sjl 		return (0);
113325cf1a30Sjl 	}
113425cf1a30Sjl 
1135738dd194Shyw 	rv = mcaddr_to_pa(mcp, &rsaddr_info->mi_restartaddr, &pa);
113625cf1a30Sjl 	if (rv != 0) {
113725cf1a30Sjl 		MC_LOG("cannot convert mcaddr to pa. use auto restart\n");
113825cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
113925cf1a30Sjl 		return (0);
114025cf1a30Sjl 	}
114125cf1a30Sjl 
1142738dd194Shyw 	if (!mc_rangecheck_pa(mcp, pa)) {
114325cf1a30Sjl 		/* pa is not on this board, just retry */
114425cf1a30Sjl 		cmn_err(CE_WARN, "restart_patrol: invalid address %lx "
114525cf1a30Sjl 			"on board %d\n", pa, mcp->mc_board_num);
114625cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
114725cf1a30Sjl 		return (0);
114825cf1a30Sjl 	}
114925cf1a30Sjl 
115025cf1a30Sjl 	MC_LOG("restart_patrol: pa = %lx\n", pa);
115125cf1a30Sjl 
1152738dd194Shyw 	if (!rsaddr_info->mi_injectrestart) {
1153738dd194Shyw 		/*
1154738dd194Shyw 		 * For non-errorinjection restart we need to
1155738dd194Shyw 		 * determine if the current restart pa/page is
1156738dd194Shyw 		 * a "good" page. A "good" page is a page that
1157738dd194Shyw 		 * has not been page retired. If the current
1158738dd194Shyw 		 * page that contains the pa is "good", we will
1159738dd194Shyw 		 * do a HW auto restart and let HW patrol continue
1160738dd194Shyw 		 * where it last stopped. Most desired scenario.
1161738dd194Shyw 		 *
1162738dd194Shyw 		 * If the current page is not "good", we will advance
1163738dd194Shyw 		 * to the next page to find the next "good" page and
1164738dd194Shyw 		 * restart the patrol from there.
1165738dd194Shyw 		 */
1166738dd194Shyw 		int wrapcount = 0;
1167738dd194Shyw 		uint64_t origpa = pa;
1168738dd194Shyw 		while (wrapcount < 2) {
1169738dd194Shyw 		    if (!pa_is_valid(mcp, pa)) {
1170738dd194Shyw 			/*
1171738dd194Shyw 			 * Not in physinstall - advance to the
1172738dd194Shyw 			 * next memory isolation blocksize
1173738dd194Shyw 			 */
117425cf1a30Sjl 			MC_LOG("Invalid PA\n");
1175738dd194Shyw 			pa = roundup(pa + 1, mc_isolation_bsize);
1176738dd194Shyw 		    } else {
1177738dd194Shyw 			int rv;
1178738dd194Shyw 			if ((rv = page_retire_check(pa, NULL)) != 0 &&
1179738dd194Shyw 			    rv != EAGAIN) {
1180738dd194Shyw 				/*
1181738dd194Shyw 				 * The page is "good" (not retired), we will
1182738dd194Shyw 				 * use automatic HW restart algorithm if
1183738dd194Shyw 				 * this is the original current starting page
1184738dd194Shyw 				 */
1185738dd194Shyw 				if (pa == origpa) {
1186738dd194Shyw 				    MC_LOG("Page has no error. Auto restart\n");
1187738dd194Shyw 				    MAC_PTRL_START(mcp, bank);
1188738dd194Shyw 				    return (0);
1189738dd194Shyw 				} else {
1190738dd194Shyw 				    /* found a subsequent good page */
1191738dd194Shyw 				    break;
1192738dd194Shyw 				}
119325cf1a30Sjl 			}
1194738dd194Shyw 
1195cfb9e062Shyw 			/*
1196738dd194Shyw 			 * Skip to the next page
1197cfb9e062Shyw 			 */
1198cfb9e062Shyw 			pa = roundup(pa + 1, PAGESIZE);
1199cfb9e062Shyw 			MC_LOG("Skipping bad page to %lx\n", pa);
1200738dd194Shyw 		    }
1201738dd194Shyw 
1202738dd194Shyw 		    /* Check to see if we hit the end of the memory range */
1203738dd194Shyw 		    if (pa >= (mcp->mc_start_address + mcp->mc_size)) {
120425cf1a30Sjl 			MC_LOG("Wrap around\n");
120525cf1a30Sjl 			pa = mcp->mc_start_address;
1206738dd194Shyw 			wrapcount++;
1207738dd194Shyw 		    }
1208738dd194Shyw 		}
1209738dd194Shyw 
1210738dd194Shyw 		if (wrapcount > 1) {
1211738dd194Shyw 		    MC_LOG("Failed to find a good page. Just restart\n");
1212738dd194Shyw 		    MAC_PTRL_START(mcp, bank);
1213738dd194Shyw 		    return (0);
121425cf1a30Sjl 		}
121525cf1a30Sjl 	}
121625cf1a30Sjl 
1217738dd194Shyw 	/*
1218738dd194Shyw 	 * We reached here either:
1219738dd194Shyw 	 * 1. We are doing an error injection restart that specify
1220738dd194Shyw 	 *    the exact pa/page to restart. OR
1221738dd194Shyw 	 * 2. We found a subsequent good page different from the
1222738dd194Shyw 	 *    original restart pa/page.
1223738dd194Shyw 	 * Restart MAC patrol: PA[37:6]
1224738dd194Shyw 	 */
122525cf1a30Sjl 	MC_LOG("restart at pa = %lx\n", pa);
122625cf1a30Sjl 	ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa));
122725cf1a30Sjl 	MAC_PTRL_START_ADD(mcp, bank);
122825cf1a30Sjl 
122925cf1a30Sjl 	return (0);
123025cf1a30Sjl }
123125cf1a30Sjl 
123225cf1a30Sjl /*
123325cf1a30Sjl  * Rewriting is used for two purposes.
123425cf1a30Sjl  *  - to correct the error in memory.
123525cf1a30Sjl  *  - to determine whether the error is permanent or intermittent.
123625cf1a30Sjl  * It's done by writing the address in MAC_BANKm_REWRITE_ADD
123725cf1a30Sjl  * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that,
123825cf1a30Sjl  * REW_END (and REW_CE/REW_UE if some error detected) is set when
123925cf1a30Sjl  * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM.
124025cf1a30Sjl  *
124125cf1a30Sjl  * Note that rewrite operation doesn't change RAW_UE to Marked UE.
124225cf1a30Sjl  * Therefore, we use it only CE case.
124325cf1a30Sjl  */
124425cf1a30Sjl static uint32_t
124525cf1a30Sjl do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr)
124625cf1a30Sjl {
124725cf1a30Sjl 	uint32_t cntl;
124825cf1a30Sjl 	int count = 0;
124925cf1a30Sjl 
125025cf1a30Sjl 	/* first wait to make sure PTRL_STATUS is 0 */
12510cc8ae86Sav 	while (count++ < mc_max_rewrite_loop) {
125225cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
125325cf1a30Sjl 		if (!(cntl & MAC_CNTL_PTRL_STATUS))
125425cf1a30Sjl 			break;
12550cc8ae86Sav 		drv_usecwait(mc_rewrite_delay);
125625cf1a30Sjl 	}
12570cc8ae86Sav 	if (count >= mc_max_rewrite_loop)
125825cf1a30Sjl 		goto bad;
125925cf1a30Sjl 
126025cf1a30Sjl 	count = 0;
126125cf1a30Sjl 
126225cf1a30Sjl 	ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr);
126325cf1a30Sjl 	MAC_REW_REQ(mcp, bank);
126425cf1a30Sjl 
126525cf1a30Sjl 	do {
126625cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
12670cc8ae86Sav 		if (count++ >= mc_max_rewrite_loop) {
126825cf1a30Sjl 			goto bad;
12690cc8ae86Sav 		} else {
12700cc8ae86Sav 			drv_usecwait(mc_rewrite_delay);
12710cc8ae86Sav 		}
127225cf1a30Sjl 	/*
127325cf1a30Sjl 	 * If there are other MEMORY or PCI activities, this
127425cf1a30Sjl 	 * will be BUSY, else it should be set immediately
127525cf1a30Sjl 	 */
127625cf1a30Sjl 	} while (!(cntl & MAC_CNTL_REW_END));
127725cf1a30Sjl 
127825cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
127925cf1a30Sjl 	return (cntl);
128025cf1a30Sjl bad:
128125cf1a30Sjl 	/* This is bad.  Just reset the circuit */
128225cf1a30Sjl 	cmn_err(CE_WARN, "mc-opl rewrite timeout on /LSB%d/B%d\n",
128325cf1a30Sjl 		mcp->mc_board_num, bank);
128425cf1a30Sjl 	cntl = MAC_CNTL_REW_END;
128525cf1a30Sjl 	MAC_CMD(mcp, bank, MAC_CNTL_PTRL_RESET);
128625cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
128725cf1a30Sjl 	return (cntl);
128825cf1a30Sjl }
128925cf1a30Sjl void
129025cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp)
129125cf1a30Sjl {
12920cc8ae86Sav 	int count;
12930cc8ae86Sav 	int n = 0;
129425cf1a30Sjl 	scf_log_t *p;
129525cf1a30Sjl 	int bank;
129625cf1a30Sjl 
12970cc8ae86Sav 	for (bank = 0; bank < BANKNUM_PER_SB; bank++) {
12980cc8ae86Sav 	    while ((p = mcp->mc_scf_log[bank]) != NULL &&
12990cc8ae86Sav 		(n < mc_max_errlog_processed)) {
13000cc8ae86Sav 		ASSERT(bank == p->sl_bank);
13010cc8ae86Sav 		count = 0;
130225cf1a30Sjl 		while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank))
130325cf1a30Sjl 			& MAC_STATIC_ERR_VLD)) {
13040cc8ae86Sav 			if (count++ >= (mc_max_scf_loop)) {
130525cf1a30Sjl 				break;
130625cf1a30Sjl 			}
13070cc8ae86Sav 			drv_usecwait(mc_scf_delay);
130825cf1a30Sjl 		}
130925cf1a30Sjl 
13100cc8ae86Sav 		if (count < mc_max_scf_loop) {
131125cf1a30Sjl 			ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank),
131225cf1a30Sjl 				p->sl_err_log);
131325cf1a30Sjl 
131425cf1a30Sjl 			ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank),
131525cf1a30Sjl 				p->sl_err_add|MAC_STATIC_ERR_VLD);
131625cf1a30Sjl 			mcp->mc_scf_retry[bank] = 0;
131725cf1a30Sjl 		} else {
131825cf1a30Sjl 			/* if we try too many times, just drop the req */
13190cc8ae86Sav 			if (mcp->mc_scf_retry[bank]++ <= mc_max_scf_retry) {
132025cf1a30Sjl 				return;
132125cf1a30Sjl 			} else {
13220cc8ae86Sav 			    if ((++mc_pce_dropped & 0xff) == 0) {
13230cc8ae86Sav 				cmn_err(CE_WARN,
13240cc8ae86Sav 				    "Cannot report Permanent CE to SCF\n");
13250cc8ae86Sav 			    }
132625cf1a30Sjl 			}
132725cf1a30Sjl 		}
13280cc8ae86Sav 		n++;
13290cc8ae86Sav 		mcp->mc_scf_log[bank] = p->sl_next;
13300cc8ae86Sav 		mcp->mc_scf_total[bank]--;
13310cc8ae86Sav 		ASSERT(mcp->mc_scf_total[bank] >= 0);
133225cf1a30Sjl 		kmem_free(p, sizeof (scf_log_t));
13330cc8ae86Sav 	    }
133425cf1a30Sjl 	}
133525cf1a30Sjl }
133625cf1a30Sjl void
133725cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank)
133825cf1a30Sjl {
133925cf1a30Sjl 	scf_log_t *p;
134025cf1a30Sjl 
13410cc8ae86Sav 	if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) {
13420cc8ae86Sav 		if ((++mc_pce_dropped & 0xff) == 0) {
13430cc8ae86Sav 		    cmn_err(CE_WARN, "Too many Permanent CE requests.\n");
13440cc8ae86Sav 		}
134525cf1a30Sjl 		return;
134625cf1a30Sjl 	}
134725cf1a30Sjl 	p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP);
134825cf1a30Sjl 	p->sl_next = 0;
134925cf1a30Sjl 	p->sl_err_add = flt_stat->mf_err_add;
135025cf1a30Sjl 	p->sl_err_log = flt_stat->mf_err_log;
135125cf1a30Sjl 	p->sl_bank = bank;
135225cf1a30Sjl 
13530cc8ae86Sav 	if (mcp->mc_scf_log[bank] == NULL) {
135425cf1a30Sjl 		/*
135525cf1a30Sjl 		 * we rely on mc_scf_log to detect NULL queue.
135625cf1a30Sjl 		 * mc_scf_log_tail is irrelevant is such case.
135725cf1a30Sjl 		 */
13580cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p;
135925cf1a30Sjl 	} else {
13600cc8ae86Sav 		mcp->mc_scf_log_tail[bank]->sl_next = p;
13610cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = p;
136225cf1a30Sjl 	}
13630cc8ae86Sav 	mcp->mc_scf_total[bank]++;
136425cf1a30Sjl }
136525cf1a30Sjl /*
136625cf1a30Sjl  * This routine determines what kind of CE happens, intermittent
136725cf1a30Sjl  * or permanent as follows. (See 4.7.3 in Columbus2 PRM.)
136825cf1a30Sjl  * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register.
136925cf1a30Sjl  * - If CE is still detected on the same address even after doing
137025cf1a30Sjl  *   rewrite operation twice, it is determined as permanent error.
137125cf1a30Sjl  * - If error is not detected anymore, it is determined as intermittent
137225cf1a30Sjl  *   error.
137325cf1a30Sjl  * - If UE is detected due to rewrite operation, it should be treated
137425cf1a30Sjl  *   as UE.
137525cf1a30Sjl  */
137625cf1a30Sjl 
137725cf1a30Sjl /* ARGSUSED */
137825cf1a30Sjl static void
137925cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error)
138025cf1a30Sjl {
138125cf1a30Sjl 	uint32_t cntl;
138225cf1a30Sjl 	int i;
138325cf1a30Sjl 
138425cf1a30Sjl 	flt_stat->mf_type = FLT_TYPE_PERMANENT_CE;
138525cf1a30Sjl 	/*
138625cf1a30Sjl 	 * rewrite request 1st time reads and correct error data
138725cf1a30Sjl 	 * and write to DIMM.  2nd rewrite request must be issued
138825cf1a30Sjl 	 * after REW_CE/UE/END is 0.  When the 2nd request is completed,
138925cf1a30Sjl 	 * if REW_CE = 1, then it is permanent CE.
139025cf1a30Sjl 	 */
139125cf1a30Sjl 	for (i = 0; i < 2; i++) {
139225cf1a30Sjl 		cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add);
139325cf1a30Sjl 		/*
139425cf1a30Sjl 		 * If the error becomes UE or CMPE
139525cf1a30Sjl 		 * we return to the caller immediately.
139625cf1a30Sjl 		 */
139725cf1a30Sjl 		if (cntl & MAC_CNTL_REW_UE) {
139825cf1a30Sjl 			if (ptrl_error)
139925cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE;
140025cf1a30Sjl 			else
140125cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_UE;
140225cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_UE;
140325cf1a30Sjl 			return;
140425cf1a30Sjl 		}
140525cf1a30Sjl 		if (cntl & MAC_CNTL_REW_CMPE) {
140625cf1a30Sjl 			if (ptrl_error)
140725cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE;
140825cf1a30Sjl 			else
140925cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE;
141025cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_CMPE;
141125cf1a30Sjl 			return;
141225cf1a30Sjl 		}
141325cf1a30Sjl 	}
141425cf1a30Sjl 	if (!(cntl & MAC_CNTL_REW_CE)) {
141525cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE;
141625cf1a30Sjl 	}
141725cf1a30Sjl 
141825cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
141925cf1a30Sjl 		/* report PERMANENT_CE to SP via SCF */
142025cf1a30Sjl 		if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) {
142125cf1a30Sjl 			mc_queue_scf_log(mcp, flt_stat, bank);
142225cf1a30Sjl 		}
142325cf1a30Sjl 	}
142425cf1a30Sjl }
142525cf1a30Sjl 
142625cf1a30Sjl #define	IS_CMPE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\
142725cf1a30Sjl 				MAC_CNTL_MI_CMPE))
142825cf1a30Sjl #define	IS_UE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE))
142925cf1a30Sjl #define	IS_CE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE))
143025cf1a30Sjl #define	IS_OK(cntl, f)	(!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \
143125cf1a30Sjl 			MAC_CNTL_MI_ERRS)))
143225cf1a30Sjl 
143325cf1a30Sjl 
143425cf1a30Sjl static int
143525cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error)
143625cf1a30Sjl {
143725cf1a30Sjl 	if (ptrl_error) {
143825cf1a30Sjl 		return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE);
143925cf1a30Sjl 	} else {
144025cf1a30Sjl 		return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE);
144125cf1a30Sjl 	}
144225cf1a30Sjl }
144325cf1a30Sjl 
144425cf1a30Sjl void
144525cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value)
144625cf1a30Sjl {
144737afe445Shyw 	int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
144837afe445Shyw 
144937afe445Shyw 	if (mcp->mc_speedup_period[ebank] > 0)
14500cc8ae86Sav 		value |= mc_max_speed;
14510cc8ae86Sav 	else
14520cc8ae86Sav 		value |= mcp->mc_speed;
145325cf1a30Sjl 	ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value);
145425cf1a30Sjl }
145525cf1a30Sjl 
145625cf1a30Sjl static void
145725cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
145825cf1a30Sjl {
145925cf1a30Sjl 	flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
146025cf1a30Sjl 		MAC_CNTL_PTRL_ERRS;
146125cf1a30Sjl 	flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank));
146225cf1a30Sjl 	flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank));
146325cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
1464*aeb241b2Sav 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
146525cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
146625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
146725cf1a30Sjl }
146825cf1a30Sjl 
146925cf1a30Sjl static void
147025cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
147125cf1a30Sjl {
147225cf1a30Sjl 	uint32_t status, old_status;
147325cf1a30Sjl 
147425cf1a30Sjl 	status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
147525cf1a30Sjl 		MAC_CNTL_MI_ERRS;
147625cf1a30Sjl 	old_status = 0;
147725cf1a30Sjl 
147825cf1a30Sjl 	/* we keep reading until the status is stable */
147925cf1a30Sjl 	while (old_status != status) {
148025cf1a30Sjl 		old_status = status;
148125cf1a30Sjl 		flt_stat->mf_err_add =
148225cf1a30Sjl 			LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank));
148325cf1a30Sjl 		flt_stat->mf_err_log =
148425cf1a30Sjl 			LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank));
148525cf1a30Sjl 		status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
148625cf1a30Sjl 			MAC_CNTL_MI_ERRS;
148725cf1a30Sjl 		if (status == old_status) {
148825cf1a30Sjl 			break;
148925cf1a30Sjl 		}
149025cf1a30Sjl 	}
149125cf1a30Sjl 
149225cf1a30Sjl 	flt_stat->mf_cntl = status;
149325cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
1494*aeb241b2Sav 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
149525cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
149625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
149725cf1a30Sjl }
149825cf1a30Sjl 
149925cf1a30Sjl 
150025cf1a30Sjl /*
150125cf1a30Sjl  * Error philosophy for mirror mode:
150225cf1a30Sjl  *
150325cf1a30Sjl  * PTRL (The error address for both banks are same, since ptrl stops if it
150425cf1a30Sjl  * detects error.)
150525cf1a30Sjl  * - Compaire error  Report CMPE.
150625cf1a30Sjl  *
150725cf1a30Sjl  * - UE-UE           Report MUE.  No rewrite.
150825cf1a30Sjl  *
150925cf1a30Sjl  * - UE-*	     UE-(CE/OK). Rewrite to scrub UE.  Report SUE.
151025cf1a30Sjl  *
151125cf1a30Sjl  * - CE-*            CE-(CE/OK). Scrub to determine if CE is permanent.
151225cf1a30Sjl  *                   If CE is permanent, inform SCF.  Once for each
151325cf1a30Sjl  *		     Dimm.  If CE becomes UE or CMPE, go back to above.
151425cf1a30Sjl  *
151525cf1a30Sjl  *
151625cf1a30Sjl  * MI (The error addresses for each bank are the same or different.)
151725cf1a30Sjl  * - Compair  error  If addresses are the same.  Just CMPE.
151825cf1a30Sjl  *		     If addresses are different (this could happen
151925cf1a30Sjl  *		     as a result of scrubbing.  Report each seperately.
152025cf1a30Sjl  *		     Only report error info on each side.
152125cf1a30Sjl  *
152225cf1a30Sjl  * - UE-UE           Addresses are the same.  Report MUE.
152325cf1a30Sjl  *		     Addresses are different.  Report SUE on each bank.
152425cf1a30Sjl  *		     Rewrite to clear UE.
152525cf1a30Sjl  *
152625cf1a30Sjl  * - UE-*	     UE-(CE/OK)
152725cf1a30Sjl  *		     Rewrite to clear UE.  Report SUE for the bank.
152825cf1a30Sjl  *
152925cf1a30Sjl  * - CE-*            CE-(CE/OK).  Scrub to determine if CE is permanent.
153025cf1a30Sjl  *                   If CE becomes UE or CMPE, go back to above.
153125cf1a30Sjl  *
153225cf1a30Sjl  */
153325cf1a30Sjl 
153425cf1a30Sjl static int
153525cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat)
153625cf1a30Sjl {
153725cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
153825cf1a30Sjl 	int i;
153925cf1a30Sjl 	int rv = 0;
154025cf1a30Sjl 
154125cf1a30Sjl 	MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n",
154225cf1a30Sjl 		flt_stat[0].mf_cntl, flt_stat[1].mf_cntl);
154325cf1a30Sjl 
154425cf1a30Sjl 	if (ptrl_error) {
154525cf1a30Sjl 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl)
154625cf1a30Sjl 			& MAC_CNTL_PTRL_ERRS) == 0)
154725cf1a30Sjl 			return (0);
154825cf1a30Sjl 	} else {
154925cf1a30Sjl 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl)
155025cf1a30Sjl 			& MAC_CNTL_MI_ERRS) == 0)
155125cf1a30Sjl 			return (0);
155225cf1a30Sjl 	}
155325cf1a30Sjl 
155425cf1a30Sjl 	/*
155525cf1a30Sjl 	 * First we take care of the case of CE
155625cf1a30Sjl 	 * because they can become UE or CMPE
155725cf1a30Sjl 	 */
155825cf1a30Sjl 	for (i = 0; i < 2; i++) {
155925cf1a30Sjl 		if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) {
156025cf1a30Sjl 			MC_LOG("CE detected on bank %d\n",
156125cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_bank);
156225cf1a30Sjl 			mc_scrub_ce(mcp, flt_stat[i].mf_flt_maddr.ma_bank,
156325cf1a30Sjl 				&flt_stat[i], ptrl_error);
156425cf1a30Sjl 			rv = 1;
156525cf1a30Sjl 		}
156625cf1a30Sjl 	}
156725cf1a30Sjl 
156825cf1a30Sjl 	/* The above scrubbing can turn CE into UE or CMPE */
156925cf1a30Sjl 
157025cf1a30Sjl 	/*
157125cf1a30Sjl 	 * Now we distinguish two cases: same address or not
157225cf1a30Sjl 	 * the same address.  It might seem more intuitive to
157325cf1a30Sjl 	 * distinguish PTRL v.s. MI error but it is more
157425cf1a30Sjl 	 * complicated that way.
157525cf1a30Sjl 	 */
157625cf1a30Sjl 
157725cf1a30Sjl 	if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) {
157825cf1a30Sjl 
157925cf1a30Sjl 		if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) ||
158025cf1a30Sjl 		    IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) {
158125cf1a30Sjl 			flt_stat[0].mf_type = FLT_TYPE_CMPE;
158225cf1a30Sjl 			flt_stat[1].mf_type = FLT_TYPE_CMPE;
158325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
158425cf1a30Sjl 			MC_LOG("cmpe error detected\n");
158525cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
158625cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
158725cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
158825cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
158925cf1a30Sjl 			mc_err_drain(mc_aflt);
159025cf1a30Sjl 			return (1);
159125cf1a30Sjl 		}
159225cf1a30Sjl 
159325cf1a30Sjl 		if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) &&
159425cf1a30Sjl 			IS_UE(flt_stat[1].mf_cntl, ptrl_error)) {
159525cf1a30Sjl 			/* Both side are UE's */
159625cf1a30Sjl 
159725cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[0]);
159825cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[1]);
159925cf1a30Sjl 			MC_LOG("MUE detected\n");
16000cc8ae86Sav 			flt_stat[0].mf_type = FLT_TYPE_MUE;
16010cc8ae86Sav 			flt_stat[1].mf_type = FLT_TYPE_MUE;
160225cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_MUE;
160325cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
160425cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
160525cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
160625cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
160725cf1a30Sjl 			mc_err_drain(mc_aflt);
160825cf1a30Sjl 			return (1);
160925cf1a30Sjl 		}
161025cf1a30Sjl 
161125cf1a30Sjl 		/* Now the only case is UE/CE, UE/OK, or don't care */
161225cf1a30Sjl 		for (i = 0; i < 2; i++) {
161325cf1a30Sjl 		    if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
16140cc8ae86Sav 
16150cc8ae86Sav 			/* rewrite can clear the one side UE error */
16160cc8ae86Sav 
161725cf1a30Sjl 			if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) {
161825cf1a30Sjl 				(void) do_rewrite(mcp,
161925cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_bank,
162025cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr);
162125cf1a30Sjl 			}
162225cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
162325cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
162425cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
162525cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
162625cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
162725cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
162825cf1a30Sjl 			mc_err_drain(mc_aflt);
162925cf1a30Sjl 			/* Once we hit a UE/CE or UE/OK case, done */
163025cf1a30Sjl 			return (1);
163125cf1a30Sjl 		    }
163225cf1a30Sjl 		}
163325cf1a30Sjl 
163425cf1a30Sjl 	} else {
163525cf1a30Sjl 		/*
163625cf1a30Sjl 		 * addresses are different. That means errors
163725cf1a30Sjl 		 * on the 2 banks are not related at all.
163825cf1a30Sjl 		 */
163925cf1a30Sjl 		for (i = 0; i < 2; i++) {
164025cf1a30Sjl 		    if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) {
164125cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_CMPE;
164225cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
164325cf1a30Sjl 			MC_LOG("cmpe error detected\n");
164425cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
164525cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
164625cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
164725cf1a30Sjl 			mc_err_drain(mc_aflt);
164825cf1a30Sjl 			/* no more report on this bank */
164925cf1a30Sjl 			flt_stat[i].mf_cntl = 0;
165025cf1a30Sjl 			rv = 1;
165125cf1a30Sjl 		    }
165225cf1a30Sjl 		}
165325cf1a30Sjl 
16540cc8ae86Sav 		/* rewrite can clear the one side UE error */
16550cc8ae86Sav 
165625cf1a30Sjl 		for (i = 0; i < 2; i++) {
165725cf1a30Sjl 		    if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
165825cf1a30Sjl 			(void) do_rewrite(mcp,
165925cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_bank,
166025cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_dimm_addr);
166125cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
166225cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
166325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
166425cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
166525cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
166625cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
166725cf1a30Sjl 			mc_err_drain(mc_aflt);
166825cf1a30Sjl 			rv = 1;
166925cf1a30Sjl 		    }
167025cf1a30Sjl 		}
167125cf1a30Sjl 	}
167225cf1a30Sjl 	return (rv);
167325cf1a30Sjl }
167425cf1a30Sjl static void
1675738dd194Shyw mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
167625cf1a30Sjl {
167725cf1a30Sjl 	mc_aflt_t mc_aflt;
167825cf1a30Sjl 	mc_flt_stat_t flt_stat[2], mi_flt_stat[2];
16790cc8ae86Sav 	int i;
16800cc8ae86Sav 	int mi_valid;
168125cf1a30Sjl 
1682738dd194Shyw 	ASSERT(rsaddr);
1683738dd194Shyw 
168425cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
168525cf1a30Sjl 	bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t));
168625cf1a30Sjl 	bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t));
168725cf1a30Sjl 
168825cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
168925cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
169025cf1a30Sjl 
169125cf1a30Sjl 	/* Now read all the registers into flt_stat */
169225cf1a30Sjl 
16930cc8ae86Sav 	for (i = 0; i < 2; i++) {
16940cc8ae86Sav 		MC_LOG("Reading registers of bank %d\n", bank);
16950cc8ae86Sav 		/* patrol registers */
16960cc8ae86Sav 		mc_read_ptrl_reg(mcp, bank, &flt_stat[i]);
169725cf1a30Sjl 
1698738dd194Shyw 		/*
1699738dd194Shyw 		 * In mirror mode, it is possible that only one bank
1700738dd194Shyw 		 * may report the error. We need to check for it to
1701738dd194Shyw 		 * ensure we pick the right addr value for patrol restart.
1702738dd194Shyw 		 * Note that if both banks reported errors, we pick the
1703738dd194Shyw 		 * 2nd one. Both banks should reported the same error address.
1704738dd194Shyw 		 */
1705738dd194Shyw 		if (flt_stat[i].mf_cntl & MAC_CNTL_PTRL_ERRS)
1706738dd194Shyw 			rsaddr->mi_restartaddr = flt_stat[i].mf_flt_maddr;
170725cf1a30Sjl 
17080cc8ae86Sav 		MC_LOG("ptrl registers cntl %x add %x log %x\n",
17090cc8ae86Sav 			flt_stat[i].mf_cntl,
17100cc8ae86Sav 			flt_stat[i].mf_err_add,
17110cc8ae86Sav 			flt_stat[i].mf_err_log);
171225cf1a30Sjl 
17130cc8ae86Sav 		/* MI registers */
17140cc8ae86Sav 		mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]);
171525cf1a30Sjl 
17160cc8ae86Sav 		MC_LOG("MI registers cntl %x add %x log %x\n",
17170cc8ae86Sav 			mi_flt_stat[i].mf_cntl,
17180cc8ae86Sav 			mi_flt_stat[i].mf_err_add,
17190cc8ae86Sav 			mi_flt_stat[i].mf_err_log);
172025cf1a30Sjl 
17210cc8ae86Sav 		bank = bank^1;
17220cc8ae86Sav 	}
172325cf1a30Sjl 
172425cf1a30Sjl 	/* clear errors once we read all the registers */
17250cc8ae86Sav 	MAC_CLEAR_ERRS(mcp, bank,
172625cf1a30Sjl 		(MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
172725cf1a30Sjl 
17280cc8ae86Sav 	MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
17290cc8ae86Sav 
17300cc8ae86Sav 	/* Process MI errors first */
173125cf1a30Sjl 
17320cc8ae86Sav 	/* if not error mode, cntl1 is 0 */
17330cc8ae86Sav 	if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
17340cc8ae86Sav 		(mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
17350cc8ae86Sav 		mi_flt_stat[0].mf_cntl = 0;
17360cc8ae86Sav 
17370cc8ae86Sav 	if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
17380cc8ae86Sav 		(mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
17390cc8ae86Sav 		mi_flt_stat[1].mf_cntl = 0;
174025cf1a30Sjl 
17410cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
17420cc8ae86Sav 	mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]);
17430cc8ae86Sav 
17440cc8ae86Sav 	if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
17450cc8ae86Sav 		MAC_CNTL_PTRL_ERR_SHIFT) ==
17460cc8ae86Sav 		((mi_flt_stat[0].mf_cntl & MAC_CNTL_MI_ERRS) >>
17470cc8ae86Sav 		MAC_CNTL_MI_ERR_SHIFT)) &&
17480cc8ae86Sav 		(flt_stat[0].mf_err_add == mi_flt_stat[0].mf_err_add) &&
17490cc8ae86Sav 		(((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
17500cc8ae86Sav 		MAC_CNTL_PTRL_ERR_SHIFT) ==
17510cc8ae86Sav 		((mi_flt_stat[1].mf_cntl & MAC_CNTL_MI_ERRS) >>
17520cc8ae86Sav 		MAC_CNTL_MI_ERR_SHIFT)) &&
17530cc8ae86Sav 		(flt_stat[1].mf_err_add == mi_flt_stat[1].mf_err_add)) {
17540cc8ae86Sav #ifdef DEBUG
17550cc8ae86Sav 		MC_LOG("discarding PTRL error because "
17560cc8ae86Sav 		    "it is the same as MI\n");
17570cc8ae86Sav #endif
1758738dd194Shyw 		rsaddr->mi_valid = mi_valid;
17590cc8ae86Sav 		return;
17600cc8ae86Sav 	}
176125cf1a30Sjl 	/* if not error mode, cntl1 is 0 */
176225cf1a30Sjl 	if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
176325cf1a30Sjl 		(flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
176425cf1a30Sjl 		flt_stat[0].mf_cntl = 0;
176525cf1a30Sjl 
176625cf1a30Sjl 	if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
176725cf1a30Sjl 		(flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
176825cf1a30Sjl 		flt_stat[1].mf_cntl = 0;
176925cf1a30Sjl 
177025cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
1771738dd194Shyw 	rsaddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]);
177225cf1a30Sjl }
177325cf1a30Sjl static int
177425cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt,
177525cf1a30Sjl 	mc_flt_stat_t *flt_stat)
177625cf1a30Sjl {
177725cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
177825cf1a30Sjl 	int rv = 0;
177925cf1a30Sjl 
178025cf1a30Sjl 	mc_aflt->mflt_erpt_class = NULL;
178125cf1a30Sjl 	if (IS_UE(flt_stat->mf_cntl, ptrl_error)) {
178225cf1a30Sjl 		MC_LOG("UE deteceted\n");
178325cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_UE;
178425cf1a30Sjl 		mc_aflt->mflt_erpt_class = MC_OPL_UE;
178525cf1a30Sjl 		mc_aflt->mflt_pr = PR_UE;
178625cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
178725cf1a30Sjl 		rv = 1;
178825cf1a30Sjl 	} else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) {
178925cf1a30Sjl 		MC_LOG("CE deteceted\n");
179025cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
179125cf1a30Sjl 
179225cf1a30Sjl 		/* Error type can change after scrubing */
179325cf1a30Sjl 		mc_scrub_ce(mcp, bank, flt_stat, ptrl_error);
179425cf1a30Sjl 
179525cf1a30Sjl 		if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
179625cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CE;
179725cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
179825cf1a30Sjl 		} else if (flt_stat->mf_type == FLT_TYPE_UE) {
179925cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_UE;
180025cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
180125cf1a30Sjl 		}
180225cf1a30Sjl 		rv = 1;
180325cf1a30Sjl 	}
180425cf1a30Sjl 	MC_LOG("mc_process_error: fault type %x erpt %s\n",
180525cf1a30Sjl 		flt_stat->mf_type,
180625cf1a30Sjl 		mc_aflt->mflt_erpt_class);
180725cf1a30Sjl 	if (mc_aflt->mflt_erpt_class) {
180825cf1a30Sjl 		mc_aflt->mflt_stat[0] = flt_stat;
180925cf1a30Sjl 		mc_aflt->mflt_nflts = 1;
181025cf1a30Sjl 		mc_err_drain(mc_aflt);
181125cf1a30Sjl 	}
181225cf1a30Sjl 	return (rv);
181325cf1a30Sjl }
181425cf1a30Sjl 
181525cf1a30Sjl static void
1816738dd194Shyw mc_error_handler(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
181725cf1a30Sjl {
181825cf1a30Sjl 	mc_aflt_t mc_aflt;
181925cf1a30Sjl 	mc_flt_stat_t flt_stat, mi_flt_stat;
18200cc8ae86Sav 	int mi_valid;
182125cf1a30Sjl 
182225cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
182325cf1a30Sjl 	bzero(&flt_stat, sizeof (mc_flt_stat_t));
182425cf1a30Sjl 	bzero(&mi_flt_stat, sizeof (mc_flt_stat_t));
182525cf1a30Sjl 
182625cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
182725cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
182825cf1a30Sjl 
182925cf1a30Sjl 	/* patrol registers */
183025cf1a30Sjl 	mc_read_ptrl_reg(mcp, bank, &flt_stat);
183125cf1a30Sjl 
1832738dd194Shyw 	ASSERT(rsaddr);
1833738dd194Shyw 	rsaddr->mi_restartaddr = flt_stat.mf_flt_maddr;
183425cf1a30Sjl 
183525cf1a30Sjl 	MC_LOG("ptrl registers cntl %x add %x log %x\n",
183625cf1a30Sjl 		flt_stat.mf_cntl,
183725cf1a30Sjl 		flt_stat.mf_err_add,
183825cf1a30Sjl 		flt_stat.mf_err_log);
183925cf1a30Sjl 
184025cf1a30Sjl 	/* MI registers */
184125cf1a30Sjl 	mc_read_mi_reg(mcp, bank, &mi_flt_stat);
184225cf1a30Sjl 
18430cc8ae86Sav 
184425cf1a30Sjl 	MC_LOG("MI registers cntl %x add %x log %x\n",
184525cf1a30Sjl 		mi_flt_stat.mf_cntl,
184625cf1a30Sjl 		mi_flt_stat.mf_err_add,
184725cf1a30Sjl 		mi_flt_stat.mf_err_log);
184825cf1a30Sjl 
184925cf1a30Sjl 	/* clear errors once we read all the registers */
185025cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
185125cf1a30Sjl 
18520cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
18530cc8ae86Sav 	if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) &&
18540cc8ae86Sav 		((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
18550cc8ae86Sav 		((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
18560cc8ae86Sav 		mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat);
18570cc8ae86Sav 	}
18580cc8ae86Sav 
18590cc8ae86Sav 	if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >>
18600cc8ae86Sav 		MAC_CNTL_PTRL_ERR_SHIFT) ==
18610cc8ae86Sav 		((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) >>
18620cc8ae86Sav 		MAC_CNTL_MI_ERR_SHIFT)) &&
18630cc8ae86Sav 		(flt_stat.mf_err_add == mi_flt_stat.mf_err_add)) {
18640cc8ae86Sav #ifdef DEBUG
18650cc8ae86Sav 		MC_LOG("discarding PTRL error because "
18660cc8ae86Sav 		    "it is the same as MI\n");
18670cc8ae86Sav #endif
1868738dd194Shyw 		rsaddr->mi_valid = mi_valid;
18690cc8ae86Sav 		return;
18700cc8ae86Sav 	}
18710cc8ae86Sav 
187225cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
187325cf1a30Sjl 	if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) &&
187425cf1a30Sjl 		((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
187525cf1a30Sjl 		((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
1876738dd194Shyw 		rsaddr->mi_valid = mc_process_error(mcp, bank,
187725cf1a30Sjl 			&mc_aflt, &flt_stat);
187825cf1a30Sjl 	}
187925cf1a30Sjl }
188025cf1a30Sjl /*
188125cf1a30Sjl  *	memory patrol error handling algorithm:
188225cf1a30Sjl  *	timeout() is used to do periodic polling
188325cf1a30Sjl  *	This is the flow chart.
188425cf1a30Sjl  *	timeout ->
188525cf1a30Sjl  *	mc_check_errors()
188625cf1a30Sjl  *	    if memory bank is installed, read the status register
188725cf1a30Sjl  *	    if any error bit is set,
188825cf1a30Sjl  *	    -> mc_error_handler()
188925cf1a30Sjl  *		-> read all error regsiters
189025cf1a30Sjl  *	        -> mc_process_error()
189125cf1a30Sjl  *	            determine error type
189225cf1a30Sjl  *	            rewrite to clear error or scrub to determine CE type
189325cf1a30Sjl  *	            inform SCF on permanent CE
189425cf1a30Sjl  *	        -> mc_err_drain
189525cf1a30Sjl  *	            page offline processing
189625cf1a30Sjl  *	            -> mc_ereport_post()
189725cf1a30Sjl  */
189825cf1a30Sjl 
189925cf1a30Sjl static void
190025cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp)
190125cf1a30Sjl {
1902738dd194Shyw 	mc_rsaddr_info_t rsaddr_info;
190325cf1a30Sjl 	int i, error_count = 0;
190425cf1a30Sjl 	uint32_t stat, cntl;
19050cc8ae86Sav 	int running;
1906cfb9e062Shyw 	int wrapped;
190737afe445Shyw 	int ebk;
190825cf1a30Sjl 
190925cf1a30Sjl 	/*
191025cf1a30Sjl 	 * scan errors.
191125cf1a30Sjl 	 */
19120cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS)
19130cc8ae86Sav 		return;
19140cc8ae86Sav 
191525cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
191625cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
191725cf1a30Sjl 			stat = ldphysio(MAC_PTRL_STAT(mcp, i));
191825cf1a30Sjl 			cntl = ldphysio(MAC_PTRL_CNTL(mcp, i));
19190cc8ae86Sav 			running = cntl & MAC_CNTL_PTRL_START;
1920cfb9e062Shyw 			wrapped = cntl & MAC_CNTL_PTRL_ADD_MAX;
19210cc8ae86Sav 
192237afe445Shyw 			/* Compute the effective bank idx */
192337afe445Shyw 			ebk = (IS_MIRROR(mcp, i)) ? MIRROR_IDX(i) : i;
192437afe445Shyw 
1925cfb9e062Shyw 			if (mc_debug_show_all || stat) {
1926cfb9e062Shyw 				MC_LOG("/LSB%d/B%d stat %x cntl %x\n",
1927cfb9e062Shyw 					mcp->mc_board_num, i,
1928cfb9e062Shyw 					stat, cntl);
1929cfb9e062Shyw 			}
1930cfb9e062Shyw 
1931cfb9e062Shyw 			/*
1932cfb9e062Shyw 			 * Update stats and reset flag if the HW patrol
1933cfb9e062Shyw 			 * wrapped around in its scan.
1934cfb9e062Shyw 			 */
1935cfb9e062Shyw 			if (wrapped) {
193625cf1a30Sjl 				MAC_CLEAR_MAX(mcp, i);
193737afe445Shyw 				mcp->mc_period[ebk]++;
193837afe445Shyw 				if (IS_MIRROR(mcp, i))
193937afe445Shyw 				    MC_LOG("mirror mc period %ld on "
194037afe445Shyw 					"/LSB%d/B%d\n", mcp->mc_period[ebk],
194137afe445Shyw 					mcp->mc_board_num, i);
194237afe445Shyw 				else {
194337afe445Shyw 				    MC_LOG("mc period %ld on "
194437afe445Shyw 					"/LSB%d/B%d\n", mcp->mc_period[ebk],
194537afe445Shyw 					mcp->mc_board_num, i);
194637afe445Shyw 				}
194725cf1a30Sjl 			}
1948cfb9e062Shyw 
1949cfb9e062Shyw 			if (running) {
1950cfb9e062Shyw 				/*
1951cfb9e062Shyw 				 * Mac patrol HW is still running.
1952cfb9e062Shyw 				 * Normally when an error is detected,
1953cfb9e062Shyw 				 * the HW patrol will stop so that we
1954cfb9e062Shyw 				 * can collect error data for reporting.
1955cfb9e062Shyw 				 * Certain errors (MI errors) detected may not
1956cfb9e062Shyw 				 * cause the HW patrol to stop which is a
1957cfb9e062Shyw 				 * problem since we cannot read error data while
1958cfb9e062Shyw 				 * the HW patrol is running. SW is not allowed
1959cfb9e062Shyw 				 * to stop the HW patrol while it is running
1960cfb9e062Shyw 				 * as it may cause HW inconsistency. This is
1961cfb9e062Shyw 				 * described in a HW errata.
1962cfb9e062Shyw 				 * In situations where we detected errors
1963cfb9e062Shyw 				 * that may not cause the HW patrol to stop.
1964cfb9e062Shyw 				 * We speed up the HW patrol scanning in
1965cfb9e062Shyw 				 * the hope that it will find the 'real' PTRL
1966cfb9e062Shyw 				 * errors associated with the previous errors
1967cfb9e062Shyw 				 * causing the HW to finally stop so that we
1968cfb9e062Shyw 				 * can do the reporting.
1969cfb9e062Shyw 				 */
1970cfb9e062Shyw 				/*
1971cfb9e062Shyw 				 * Check to see if we did speed up
1972cfb9e062Shyw 				 * the HW patrol due to previous errors
1973cfb9e062Shyw 				 * detected that did not cause the patrol
1974cfb9e062Shyw 				 * to stop. We only do it if HW patrol scan
1975cfb9e062Shyw 				 * wrapped (counted as completing a 'period').
1976cfb9e062Shyw 				 */
197737afe445Shyw 				if (mcp->mc_speedup_period[ebk] > 0) {
1978cfb9e062Shyw 				    if (wrapped &&
197937afe445Shyw 					(--mcp->mc_speedup_period[ebk] == 0)) {
1980cfb9e062Shyw 					/*
1981cfb9e062Shyw 					 * We did try to speed up.
1982cfb9e062Shyw 					 * The speed up period has expired
1983cfb9e062Shyw 					 * and the HW patrol is still running.
1984cfb9e062Shyw 					 * The errors must be intermittent.
1985cfb9e062Shyw 					 * We have no choice but to ignore
1986cfb9e062Shyw 					 * them, reset the scan speed to normal
198737afe445Shyw 					 * and clear the MI error bits. For
198837afe445Shyw 					 * mirror mode, we need to clear errors
198937afe445Shyw 					 * on both banks.
1990cfb9e062Shyw 					 */
1991cfb9e062Shyw 					MC_LOG("Clearing MI errors\n");
1992cfb9e062Shyw 					MAC_CLEAR_ERRS(mcp, i,
1993cfb9e062Shyw 					    MAC_CNTL_MI_ERRS);
199437afe445Shyw 
199537afe445Shyw 					if (IS_MIRROR(mcp, i)) {
199637afe445Shyw 					    MC_LOG("Clearing Mirror MI errs\n");
199737afe445Shyw 					    MAC_CLEAR_ERRS(mcp, i^1,
199837afe445Shyw 						MAC_CNTL_MI_ERRS);
199937afe445Shyw 					}
2000cfb9e062Shyw 				    }
2001cfb9e062Shyw 				} else if (stat & MAC_STAT_MI_ERRS) {
2002cfb9e062Shyw 					/*
2003cfb9e062Shyw 					 * MI errors detected but we cannot
2004cfb9e062Shyw 					 * report them since the HW patrol
2005cfb9e062Shyw 					 * is still running.
2006cfb9e062Shyw 					 * We will attempt to speed up the
2007cfb9e062Shyw 					 * scanning and hopefully the HW
2008cfb9e062Shyw 					 * can detect PRTL errors at the same
2009cfb9e062Shyw 					 * location that cause the HW patrol
2010cfb9e062Shyw 					 * to stop.
2011cfb9e062Shyw 					 */
201237afe445Shyw 					mcp->mc_speedup_period[ebk] = 2;
20130cc8ae86Sav 					MAC_CMD(mcp, i, 0);
20140cc8ae86Sav 				}
2015cfb9e062Shyw 			} else if (stat & (MAC_STAT_PTRL_ERRS |
2016cfb9e062Shyw 			    MAC_STAT_MI_ERRS)) {
2017cfb9e062Shyw 				/*
2018cfb9e062Shyw 				 * HW Patrol has stopped and we found errors.
2019cfb9e062Shyw 				 * Proceed to collect and report error info.
2020cfb9e062Shyw 				 */
202137afe445Shyw 				mcp->mc_speedup_period[ebk] = 0;
2022738dd194Shyw 				rsaddr_info.mi_valid = 0;
2023738dd194Shyw 				rsaddr_info.mi_injectrestart = 0;
2024738dd194Shyw 				if (IS_MIRROR(mcp, i)) {
2025738dd194Shyw 				    mc_error_handler_mir(mcp, i, &rsaddr_info);
2026738dd194Shyw 				} else {
2027738dd194Shyw 				    mc_error_handler(mcp, i, &rsaddr_info);
2028738dd194Shyw 				}
2029cfb9e062Shyw 
2030cfb9e062Shyw 				error_count++;
2031738dd194Shyw 				restart_patrol(mcp, i, &rsaddr_info);
203225cf1a30Sjl 			} else {
2033cfb9e062Shyw 				/*
2034cfb9e062Shyw 				 * HW patrol scan has apparently stopped
2035cfb9e062Shyw 				 * but no errors detected/flagged.
2036cfb9e062Shyw 				 * Restart the HW patrol just to be sure.
203737afe445Shyw 				 * In mirror mode, the odd bank might have
203837afe445Shyw 				 * reported errors that caused the patrol to
203937afe445Shyw 				 * stop. We'll defer the restart to the odd
204037afe445Shyw 				 * bank in this case.
2041cfb9e062Shyw 				 */
204237afe445Shyw 				if (!IS_MIRROR(mcp, i) || (i & 0x1))
204337afe445Shyw 					restart_patrol(mcp, i, NULL);
204425cf1a30Sjl 			}
204525cf1a30Sjl 		}
204625cf1a30Sjl 	}
204725cf1a30Sjl 	if (error_count > 0)
204825cf1a30Sjl 		mcp->mc_last_error += error_count;
204925cf1a30Sjl 	else
205025cf1a30Sjl 		mcp->mc_last_error = 0;
205125cf1a30Sjl }
205225cf1a30Sjl 
20530cc8ae86Sav /*
20540cc8ae86Sav  * mc_polling -- Check errors for only one instance,
20550cc8ae86Sav  * but process errors for all instances to make sure we drain the errors
20560cc8ae86Sav  * faster than they can be accumulated.
20570cc8ae86Sav  *
20580cc8ae86Sav  * Polling on each board should be done only once per each
20590cc8ae86Sav  * mc_patrol_interval_sec.  This is equivalent to setting mc_tick_left
20600cc8ae86Sav  * to OPL_MAX_BOARDS and decrement by 1 on each timeout.
20610cc8ae86Sav  * Once mc_tick_left becomes negative, the board becomes a candidate
20620cc8ae86Sav  * for polling because it has waited for at least
20630cc8ae86Sav  * mc_patrol_interval_sec's long.    If mc_timeout_period is calculated
20640cc8ae86Sav  * differently, this has to beupdated accordingly.
20650cc8ae86Sav  */
206625cf1a30Sjl 
206725cf1a30Sjl static void
20680cc8ae86Sav mc_polling(void)
206925cf1a30Sjl {
20700cc8ae86Sav 	int i, scan_error;
20710cc8ae86Sav 	mc_opl_t *mcp;
207225cf1a30Sjl 
207325cf1a30Sjl 
20740cc8ae86Sav 	scan_error = 1;
20750cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
20760cc8ae86Sav 		mutex_enter(&mcmutex);
20770cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL) {
20780cc8ae86Sav 			mutex_exit(&mcmutex);
20790cc8ae86Sav 			continue;
20800cc8ae86Sav 		}
20810cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
20820cc8ae86Sav 		mutex_exit(&mcmutex);
2083738dd194Shyw 		if (!(mcp->mc_status & MC_POLL_RUNNING)) {
2084738dd194Shyw 			mutex_exit(&mcp->mc_lock);
2085738dd194Shyw 			continue;
2086738dd194Shyw 		}
20870cc8ae86Sav 		if (scan_error && mcp->mc_tick_left <= 0) {
20880cc8ae86Sav 			mc_check_errors_func((void *)mcp);
20890cc8ae86Sav 			mcp->mc_tick_left = OPL_MAX_BOARDS;
20900cc8ae86Sav 			scan_error = 0;
20910cc8ae86Sav 		} else {
20920cc8ae86Sav 			mcp->mc_tick_left--;
20930cc8ae86Sav 		}
20940cc8ae86Sav 		mc_process_scf_log(mcp);
20950cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
209625cf1a30Sjl 	}
209725cf1a30Sjl }
209825cf1a30Sjl 
209925cf1a30Sjl static void
210025cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr)
210125cf1a30Sjl {
210225cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
210325cf1a30Sjl 	maddr->ma_bank = bank;
210425cf1a30Sjl 	maddr->ma_dimm_addr = 0;
210525cf1a30Sjl }
210625cf1a30Sjl 
210725cf1a30Sjl typedef struct mc_mem_range {
210825cf1a30Sjl 	uint64_t	addr;
210925cf1a30Sjl 	uint64_t	size;
211025cf1a30Sjl } mc_mem_range_t;
211125cf1a30Sjl 
211225cf1a30Sjl static int
211325cf1a30Sjl get_base_address(mc_opl_t *mcp)
211425cf1a30Sjl {
211525cf1a30Sjl 	mc_mem_range_t *mem_range;
211625cf1a30Sjl 	int len;
211725cf1a30Sjl 
211825cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
211925cf1a30Sjl 		"sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) {
212025cf1a30Sjl 		return (DDI_FAILURE);
212125cf1a30Sjl 	}
212225cf1a30Sjl 
212325cf1a30Sjl 	mcp->mc_start_address = mem_range->addr;
212425cf1a30Sjl 	mcp->mc_size = mem_range->size;
212525cf1a30Sjl 
212625cf1a30Sjl 	kmem_free(mem_range, len);
212725cf1a30Sjl 	return (DDI_SUCCESS);
212825cf1a30Sjl }
212925cf1a30Sjl 
213025cf1a30Sjl struct mc_addr_spec {
213125cf1a30Sjl 	uint32_t bank;
213225cf1a30Sjl 	uint32_t phys_hi;
213325cf1a30Sjl 	uint32_t phys_lo;
213425cf1a30Sjl };
213525cf1a30Sjl 
213625cf1a30Sjl #define	REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo)
213725cf1a30Sjl 
213825cf1a30Sjl static char *mc_tbl_name[] = {
213925cf1a30Sjl 	"cs0-mc-pa-trans-table",
214025cf1a30Sjl 	"cs1-mc-pa-trans-table"
214125cf1a30Sjl };
214225cf1a30Sjl 
2143738dd194Shyw /*
2144738dd194Shyw  * This routine performs a rangecheck for a given PA
2145738dd194Shyw  * to see if it belongs to the memory range for this board.
2146738dd194Shyw  * Return 1 if it is valid (within the range) and 0 otherwise
2147738dd194Shyw  */
214825cf1a30Sjl static int
2149738dd194Shyw mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa)
215025cf1a30Sjl {
2151738dd194Shyw 	if ((pa < mcp->mc_start_address) ||
2152738dd194Shyw 		(mcp->mc_start_address + mcp->mc_size <= pa))
2153738dd194Shyw 		return (0);
2154738dd194Shyw 	else
2155738dd194Shyw 		return (1);
215625cf1a30Sjl }
215725cf1a30Sjl 
215825cf1a30Sjl static void
215925cf1a30Sjl mc_memlist_delete(struct memlist *mlist)
216025cf1a30Sjl {
216125cf1a30Sjl 	struct memlist *ml;
216225cf1a30Sjl 
216325cf1a30Sjl 	for (ml = mlist; ml; ml = mlist) {
216425cf1a30Sjl 		mlist = ml->next;
216525cf1a30Sjl 		kmem_free(ml, sizeof (struct memlist));
216625cf1a30Sjl 	}
216725cf1a30Sjl }
216825cf1a30Sjl 
216925cf1a30Sjl static struct memlist *
217025cf1a30Sjl mc_memlist_dup(struct memlist *mlist)
217125cf1a30Sjl {
217225cf1a30Sjl 	struct memlist *hl = NULL, *tl, **mlp;
217325cf1a30Sjl 
217425cf1a30Sjl 	if (mlist == NULL)
217525cf1a30Sjl 		return (NULL);
217625cf1a30Sjl 
217725cf1a30Sjl 	mlp = &hl;
217825cf1a30Sjl 	tl = *mlp;
217925cf1a30Sjl 	for (; mlist; mlist = mlist->next) {
218025cf1a30Sjl 		*mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP);
218125cf1a30Sjl 		(*mlp)->address = mlist->address;
218225cf1a30Sjl 		(*mlp)->size = mlist->size;
218325cf1a30Sjl 		(*mlp)->prev = tl;
218425cf1a30Sjl 		tl = *mlp;
218525cf1a30Sjl 		mlp = &((*mlp)->next);
218625cf1a30Sjl 	}
218725cf1a30Sjl 	*mlp = NULL;
218825cf1a30Sjl 
218925cf1a30Sjl 	return (hl);
219025cf1a30Sjl }
219125cf1a30Sjl 
219225cf1a30Sjl 
219325cf1a30Sjl static struct memlist *
219425cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len)
219525cf1a30Sjl {
219625cf1a30Sjl 	uint64_t	end;
219725cf1a30Sjl 	struct memlist	*ml, *tl, *nlp;
219825cf1a30Sjl 
219925cf1a30Sjl 	if (mlist == NULL)
220025cf1a30Sjl 		return (NULL);
220125cf1a30Sjl 
220225cf1a30Sjl 	end = base + len;
220325cf1a30Sjl 	if ((end <= mlist->address) || (base == end))
220425cf1a30Sjl 		return (mlist);
220525cf1a30Sjl 
220625cf1a30Sjl 	for (tl = ml = mlist; ml; tl = ml, ml = nlp) {
220725cf1a30Sjl 		uint64_t	mend;
220825cf1a30Sjl 
220925cf1a30Sjl 		nlp = ml->next;
221025cf1a30Sjl 
221125cf1a30Sjl 		if (end <= ml->address)
221225cf1a30Sjl 			break;
221325cf1a30Sjl 
221425cf1a30Sjl 		mend = ml->address + ml->size;
221525cf1a30Sjl 		if (base < mend) {
221625cf1a30Sjl 			if (base <= ml->address) {
221725cf1a30Sjl 				ml->address = end;
221825cf1a30Sjl 				if (end >= mend)
221925cf1a30Sjl 					ml->size = 0ull;
222025cf1a30Sjl 				else
222125cf1a30Sjl 					ml->size = mend - ml->address;
222225cf1a30Sjl 			} else {
222325cf1a30Sjl 				ml->size = base - ml->address;
222425cf1a30Sjl 				if (end < mend) {
222525cf1a30Sjl 					struct memlist	*nl;
222625cf1a30Sjl 					/*
222725cf1a30Sjl 					 * splitting an memlist entry.
222825cf1a30Sjl 					 */
222925cf1a30Sjl 					nl = kmem_alloc(sizeof (struct memlist),
223025cf1a30Sjl 						KM_SLEEP);
223125cf1a30Sjl 					nl->address = end;
223225cf1a30Sjl 					nl->size = mend - nl->address;
223325cf1a30Sjl 					if ((nl->next = nlp) != NULL)
223425cf1a30Sjl 						nlp->prev = nl;
223525cf1a30Sjl 					nl->prev = ml;
223625cf1a30Sjl 					ml->next = nl;
223725cf1a30Sjl 					nlp = nl;
223825cf1a30Sjl 				}
223925cf1a30Sjl 			}
224025cf1a30Sjl 			if (ml->size == 0ull) {
224125cf1a30Sjl 				if (ml == mlist) {
224225cf1a30Sjl 					if ((mlist = nlp) != NULL)
224325cf1a30Sjl 						nlp->prev = NULL;
224425cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
224525cf1a30Sjl 					if (mlist == NULL)
224625cf1a30Sjl 						break;
224725cf1a30Sjl 					ml = nlp;
224825cf1a30Sjl 				} else {
224925cf1a30Sjl 					if ((tl->next = nlp) != NULL)
225025cf1a30Sjl 						nlp->prev = tl;
225125cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
225225cf1a30Sjl 					ml = tl;
225325cf1a30Sjl 				}
225425cf1a30Sjl 			}
225525cf1a30Sjl 		}
225625cf1a30Sjl 	}
225725cf1a30Sjl 
225825cf1a30Sjl 	return (mlist);
225925cf1a30Sjl }
226025cf1a30Sjl 
226125cf1a30Sjl static void
226225cf1a30Sjl mc_get_mlist(mc_opl_t *mcp)
226325cf1a30Sjl {
226425cf1a30Sjl 	struct memlist *mlist;
226525cf1a30Sjl 
226625cf1a30Sjl 	memlist_read_lock();
226725cf1a30Sjl 	mlist = mc_memlist_dup(phys_install);
226825cf1a30Sjl 	memlist_read_unlock();
226925cf1a30Sjl 
227025cf1a30Sjl 	if (mlist) {
227125cf1a30Sjl 		mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address);
227225cf1a30Sjl 	}
227325cf1a30Sjl 
227425cf1a30Sjl 	if (mlist) {
227525cf1a30Sjl 		uint64_t startpa, endpa;
227625cf1a30Sjl 
227725cf1a30Sjl 		startpa = mcp->mc_start_address + mcp->mc_size;
227825cf1a30Sjl 		endpa = ptob(physmax + 1);
227925cf1a30Sjl 		if (endpa > startpa) {
228025cf1a30Sjl 			mlist = mc_memlist_del_span(mlist,
228125cf1a30Sjl 				startpa, endpa - startpa);
228225cf1a30Sjl 		}
228325cf1a30Sjl 	}
228425cf1a30Sjl 
228525cf1a30Sjl 	if (mlist) {
228625cf1a30Sjl 		mcp->mlist = mlist;
228725cf1a30Sjl 	}
228825cf1a30Sjl }
228925cf1a30Sjl 
229025cf1a30Sjl int
229125cf1a30Sjl mc_board_add(mc_opl_t *mcp)
229225cf1a30Sjl {
229325cf1a30Sjl 	struct mc_addr_spec *macaddr;
22940cc8ae86Sav 	cs_status_t *cs_status;
22950cc8ae86Sav 	int len, len1, i, bk, cc;
2296738dd194Shyw 	mc_rsaddr_info_t rsaddr;
229725cf1a30Sjl 	uint32_t mirr;
22980cc8ae86Sav 	int nbanks = 0;
22990cc8ae86Sav 	uint64_t nbytes = 0;
230025cf1a30Sjl 
230125cf1a30Sjl 	/*
230225cf1a30Sjl 	 * Get configurations from "pseudo-mc" node which includes:
230325cf1a30Sjl 	 * board# : LSB number
230425cf1a30Sjl 	 * mac-addr : physical base address of MAC registers
230525cf1a30Sjl 	 * csX-mac-pa-trans-table: translation table from DIMM address
230625cf1a30Sjl 	 *			to physical address or vice versa.
230725cf1a30Sjl 	 */
230825cf1a30Sjl 	mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip,
230925cf1a30Sjl 		DDI_PROP_DONTPASS, "board#", -1);
231025cf1a30Sjl 
23110cc8ae86Sav 	if (mcp->mc_board_num == -1) {
23120cc8ae86Sav 		return (DDI_FAILURE);
23130cc8ae86Sav 	}
23140cc8ae86Sav 
231525cf1a30Sjl 	/*
231625cf1a30Sjl 	 * Get start address in this CAB. It can be gotten from
231725cf1a30Sjl 	 * "sb-mem-ranges" property.
231825cf1a30Sjl 	 */
231925cf1a30Sjl 
232025cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
232125cf1a30Sjl 		return (DDI_FAILURE);
232225cf1a30Sjl 	}
232325cf1a30Sjl 	/* get mac-pa trans tables */
232425cf1a30Sjl 	for (i = 0; i < MC_TT_CS; i++) {
232525cf1a30Sjl 		len = MC_TT_ENTRIES;
232625cf1a30Sjl 		cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip,
232725cf1a30Sjl 			DDI_PROP_DONTPASS, mc_tbl_name[i],
232825cf1a30Sjl 			(caddr_t)mcp->mc_trans_table[i], &len);
232925cf1a30Sjl 
233025cf1a30Sjl 		if (cc != DDI_SUCCESS) {
233125cf1a30Sjl 			bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES);
233225cf1a30Sjl 		}
233325cf1a30Sjl 	}
233425cf1a30Sjl 	mcp->mlist = NULL;
233525cf1a30Sjl 
233625cf1a30Sjl 	mc_get_mlist(mcp);
233725cf1a30Sjl 
233825cf1a30Sjl 	/* initialize bank informations */
233925cf1a30Sjl 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
234025cf1a30Sjl 		"mc-addr", (caddr_t)&macaddr, &len);
234125cf1a30Sjl 	if (cc != DDI_SUCCESS) {
234225cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc);
234325cf1a30Sjl 		return (DDI_FAILURE);
234425cf1a30Sjl 	}
234525cf1a30Sjl 
23460cc8ae86Sav 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
23470cc8ae86Sav 		"cs-status", (caddr_t)&cs_status, &len1);
234825cf1a30Sjl 
23490cc8ae86Sav 	if (cc != DDI_SUCCESS) {
23500cc8ae86Sav 		if (len > 0)
23510cc8ae86Sav 			kmem_free(macaddr, len);
23520cc8ae86Sav 		cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc);
23530cc8ae86Sav 		return (DDI_FAILURE);
23540cc8ae86Sav 	}
2355*aeb241b2Sav 	/* get the physical board number for a given logical board number */
2356*aeb241b2Sav 	mcp->mc_phys_board_num = mc_opl_get_physical_board(mcp->mc_board_num);
2357*aeb241b2Sav 
2358*aeb241b2Sav 	if (mcp->mc_phys_board_num < 0) {
2359*aeb241b2Sav 		if (len > 0)
2360*aeb241b2Sav 			kmem_free(macaddr, len);
2361*aeb241b2Sav 		cmn_err(CE_WARN, "Unable to obtain the physical board number");
2362*aeb241b2Sav 		return (DDI_FAILURE);
2363*aeb241b2Sav 	}
236425cf1a30Sjl 
23650cc8ae86Sav 	mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL);
23660cc8ae86Sav 
23670cc8ae86Sav 	for (i = 0; i < len1 / sizeof (cs_status_t); i++) {
23680cc8ae86Sav 		nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) |
23690cc8ae86Sav 			((uint64_t)cs_status[i].cs_avail_low);
23700cc8ae86Sav 	}
23710cc8ae86Sav 	if (len1 > 0)
23720cc8ae86Sav 		kmem_free(cs_status, len1);
23730cc8ae86Sav 	nbanks = len / sizeof (struct mc_addr_spec);
23740cc8ae86Sav 
23750cc8ae86Sav 	if (nbanks > 0)
23760cc8ae86Sav 		nbytes /= nbanks;
23770cc8ae86Sav 	else {
23780cc8ae86Sav 		/* No need to free macaddr because len must be 0 */
23790cc8ae86Sav 		mcp->mc_status |= MC_MEMORYLESS;
23800cc8ae86Sav 		return (DDI_SUCCESS);
23810cc8ae86Sav 	}
23820cc8ae86Sav 
23830cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
23840cc8ae86Sav 		mcp->mc_scf_retry[i] = 0;
23850cc8ae86Sav 		mcp->mc_period[i] = 0;
23860cc8ae86Sav 		mcp->mc_speedup_period[i] = 0;
23870cc8ae86Sav 	}
23880cc8ae86Sav 
23890cc8ae86Sav 	/*
23900cc8ae86Sav 	 * Get the memory size here. Let it be B (bytes).
23910cc8ae86Sav 	 * Let T be the time in u.s. to scan 64 bytes.
23920cc8ae86Sav 	 * If we want to complete 1 round of scanning in P seconds.
23930cc8ae86Sav 	 *
23940cc8ae86Sav 	 *	B * T * 10^(-6)	= P
23950cc8ae86Sav 	 *	---------------
23960cc8ae86Sav 	 *		64
23970cc8ae86Sav 	 *
23980cc8ae86Sav 	 *	T = P * 64 * 10^6
23990cc8ae86Sav 	 *	    -------------
24000cc8ae86Sav 	 *		B
24010cc8ae86Sav 	 *
24020cc8ae86Sav 	 *	  = P * 64 * 10^6
24030cc8ae86Sav 	 *	    -------------
24040cc8ae86Sav 	 *		B
24050cc8ae86Sav 	 *
24060cc8ae86Sav 	 *	The timing bits are set in PTRL_CNTL[28:26] where
24070cc8ae86Sav 	 *
24080cc8ae86Sav 	 *	0	- 1 m.s
24090cc8ae86Sav 	 *	1	- 512 u.s.
24100cc8ae86Sav 	 *	10	- 256 u.s.
24110cc8ae86Sav 	 *	11	- 128 u.s.
24120cc8ae86Sav 	 *	100	- 64 u.s.
24130cc8ae86Sav 	 *	101	- 32 u.s.
24140cc8ae86Sav 	 *	110	- 0 u.s.
24150cc8ae86Sav 	 *	111	- reserved.
24160cc8ae86Sav 	 *
24170cc8ae86Sav 	 *
24180cc8ae86Sav 	 *	a[0] = 110, a[1] = 101, ... a[6] = 0
24190cc8ae86Sav 	 *
24200cc8ae86Sav 	 *	cs-status property is int x 7
24210cc8ae86Sav 	 *	0 - cs#
24220cc8ae86Sav 	 *	1 - cs-status
24230cc8ae86Sav 	 *	2 - cs-avail.hi
24240cc8ae86Sav 	 *	3 - cs-avail.lo
24250cc8ae86Sav 	 *	4 - dimm-capa.hi
24260cc8ae86Sav 	 *	5 - dimm-capa.lo
24270cc8ae86Sav 	 *	6 - #of dimms
24280cc8ae86Sav 	 */
24290cc8ae86Sav 
24300cc8ae86Sav 	if (nbytes > 0) {
24310cc8ae86Sav 		int i;
24320cc8ae86Sav 		uint64_t ms;
24330cc8ae86Sav 		ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes;
24340cc8ae86Sav 		mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds;
24350cc8ae86Sav 		for (i = 0; i < MC_MAX_SPEEDS - 1; i++) {
24360cc8ae86Sav 			if (ms < mc_scan_speeds[i + 1].mc_period) {
24370cc8ae86Sav 				mcp->mc_speed = mc_scan_speeds[i].mc_speeds;
24380cc8ae86Sav 				break;
24390cc8ae86Sav 			}
24400cc8ae86Sav 		}
24410cc8ae86Sav 	} else
24420cc8ae86Sav 		mcp->mc_speed = 0;
24430cc8ae86Sav 
24440cc8ae86Sav 
24450cc8ae86Sav 	for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) {
24460cc8ae86Sav 		struct mc_bank *bankp;
24470cc8ae86Sav 		uint32_t reg;
24480cc8ae86Sav 
24490cc8ae86Sav 		/*
24500cc8ae86Sav 		 * setup bank
24510cc8ae86Sav 		 */
24520cc8ae86Sav 		bk = macaddr[i].bank;
24530cc8ae86Sav 		bankp = &(mcp->mc_bank[bk]);
24540cc8ae86Sav 		bankp->mcb_status = BANK_INSTALLED;
24550cc8ae86Sav 		bankp->mcb_reg_base = REGS_PA(macaddr, i);
24560cc8ae86Sav 
24570cc8ae86Sav 		reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk));
24580cc8ae86Sav 		bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS);
245925cf1a30Sjl 
246025cf1a30Sjl 		/*
246125cf1a30Sjl 		 * check if mirror mode
246225cf1a30Sjl 		 */
246325cf1a30Sjl 		mirr = LD_MAC_REG(MAC_MIRR(mcp, bk));
246425cf1a30Sjl 
246525cf1a30Sjl 		if (mirr & MAC_MIRR_MIRROR_MODE) {
246625cf1a30Sjl 			MC_LOG("Mirror -> /LSB%d/B%d\n",
246725cf1a30Sjl 				mcp->mc_board_num, bk);
246825cf1a30Sjl 			bankp->mcb_status |= BANK_MIRROR_MODE;
246925cf1a30Sjl 			/*
247025cf1a30Sjl 			 * The following bit is only used for
247125cf1a30Sjl 			 * error injection.  We should clear it
247225cf1a30Sjl 			 */
247325cf1a30Sjl 			if (mirr & MAC_MIRR_BANK_EXCLUSIVE)
247425cf1a30Sjl 				ST_MAC_REG(MAC_MIRR(mcp, bk),
247525cf1a30Sjl 					0);
247625cf1a30Sjl 		}
247725cf1a30Sjl 
247825cf1a30Sjl 		/*
247925cf1a30Sjl 		 * restart if not mirror mode or the other bank
248025cf1a30Sjl 		 * of the mirror is not running
248125cf1a30Sjl 		 */
248225cf1a30Sjl 		if (!(mirr & MAC_MIRR_MIRROR_MODE) ||
248325cf1a30Sjl 			!(mcp->mc_bank[bk^1].mcb_status &
248425cf1a30Sjl 			BANK_PTRL_RUNNING)) {
248525cf1a30Sjl 			MC_LOG("Starting up /LSB%d/B%d\n",
248625cf1a30Sjl 				mcp->mc_board_num, bk);
2487738dd194Shyw 			get_ptrl_start_address(mcp, bk, &rsaddr.mi_restartaddr);
2488738dd194Shyw 			rsaddr.mi_valid = 0;
2489738dd194Shyw 			rsaddr.mi_injectrestart = 0;
2490738dd194Shyw 			restart_patrol(mcp, bk, &rsaddr);
249125cf1a30Sjl 		} else {
249225cf1a30Sjl 			MC_LOG("Not starting up /LSB%d/B%d\n",
249325cf1a30Sjl 				mcp->mc_board_num, bk);
249425cf1a30Sjl 		}
249525cf1a30Sjl 		bankp->mcb_status |= BANK_PTRL_RUNNING;
249625cf1a30Sjl 	}
24970cc8ae86Sav 	if (len > 0)
24980cc8ae86Sav 		kmem_free(macaddr, len);
24990cc8ae86Sav 
25000cc8ae86Sav 	mcp->mc_dimm_list = mc_get_dimm_list(mcp);
250125cf1a30Sjl 
250225cf1a30Sjl 	/*
250325cf1a30Sjl 	 * set interval in HZ.
250425cf1a30Sjl 	 */
250525cf1a30Sjl 	mcp->mc_last_error = 0;
250625cf1a30Sjl 
250725cf1a30Sjl 	/* restart memory patrol checking */
250825cf1a30Sjl 	mcp->mc_status |= MC_POLL_RUNNING;
250925cf1a30Sjl 
251025cf1a30Sjl 	return (DDI_SUCCESS);
251125cf1a30Sjl }
251225cf1a30Sjl 
251325cf1a30Sjl int
251425cf1a30Sjl mc_board_del(mc_opl_t *mcp)
251525cf1a30Sjl {
251625cf1a30Sjl 	int i;
251725cf1a30Sjl 	scf_log_t *p;
251825cf1a30Sjl 
251925cf1a30Sjl 	/*
252025cf1a30Sjl 	 * cleanup mac state
252125cf1a30Sjl 	 */
252225cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
25230cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
25240cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
25250cc8ae86Sav 		mutex_destroy(&mcp->mc_lock);
25260cc8ae86Sav 		return (DDI_SUCCESS);
25270cc8ae86Sav 	}
252825cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
252925cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
253025cf1a30Sjl 			mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED;
253125cf1a30Sjl 		}
253225cf1a30Sjl 	}
253325cf1a30Sjl 
253425cf1a30Sjl 	/* stop memory patrol checking */
2535738dd194Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
253625cf1a30Sjl 
253725cf1a30Sjl 	/* just throw away all the scf logs */
25380cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
25390cc8ae86Sav 	    while ((p = mcp->mc_scf_log[i]) != NULL) {
25400cc8ae86Sav 		mcp->mc_scf_log[i] = p->sl_next;
25410cc8ae86Sav 		mcp->mc_scf_total[i]--;
254225cf1a30Sjl 		kmem_free(p, sizeof (scf_log_t));
25430cc8ae86Sav 	    }
254425cf1a30Sjl 	}
254525cf1a30Sjl 
254625cf1a30Sjl 	if (mcp->mlist)
254725cf1a30Sjl 		mc_memlist_delete(mcp->mlist);
254825cf1a30Sjl 
25490cc8ae86Sav 	if (mcp->mc_dimm_list)
25500cc8ae86Sav 		mc_free_dimm_list(mcp->mc_dimm_list);
25510cc8ae86Sav 
255225cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
255325cf1a30Sjl 
255425cf1a30Sjl 	mutex_destroy(&mcp->mc_lock);
255525cf1a30Sjl 	return (DDI_SUCCESS);
255625cf1a30Sjl }
255725cf1a30Sjl 
255825cf1a30Sjl int
255925cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag)
256025cf1a30Sjl {
256125cf1a30Sjl 	/* stop memory patrol checking */
256225cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
25630cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
25640cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
25650cc8ae86Sav 		return (DDI_SUCCESS);
25660cc8ae86Sav 	}
25670cc8ae86Sav 
2568738dd194Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
2569738dd194Shyw 
257025cf1a30Sjl 	mcp->mc_status |= flag;
257125cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
257225cf1a30Sjl 
257325cf1a30Sjl 	return (DDI_SUCCESS);
257425cf1a30Sjl }
257525cf1a30Sjl 
257625cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */
257725cf1a30Sjl 
257825cf1a30Sjl int
257925cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag)
258025cf1a30Sjl {
258125cf1a30Sjl 	int i;
258225cf1a30Sjl 	uint64_t basepa;
258325cf1a30Sjl 
258425cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
25850cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
25860cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
25870cc8ae86Sav 		return (DDI_SUCCESS);
25880cc8ae86Sav 	}
258925cf1a30Sjl 	basepa = mcp->mc_start_address;
259025cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
259125cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
259225cf1a30Sjl 		return (DDI_FAILURE);
259325cf1a30Sjl 	}
259425cf1a30Sjl 
259525cf1a30Sjl 	if (basepa != mcp->mc_start_address) {
259625cf1a30Sjl 		if (mcp->mlist)
259725cf1a30Sjl 			mc_memlist_delete(mcp->mlist);
259825cf1a30Sjl 		mcp->mlist = NULL;
259925cf1a30Sjl 		mc_get_mlist(mcp);
260025cf1a30Sjl 	}
260125cf1a30Sjl 
260225cf1a30Sjl 	mcp->mc_status &= ~flag;
260325cf1a30Sjl 
260425cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
260525cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
260625cf1a30Sjl 		return (DDI_SUCCESS);
260725cf1a30Sjl 	}
260825cf1a30Sjl 
260925cf1a30Sjl 	if (!(mcp->mc_status & MC_POLL_RUNNING)) {
261025cf1a30Sjl 		/* restart memory patrol checking */
261125cf1a30Sjl 		mcp->mc_status |= MC_POLL_RUNNING;
261225cf1a30Sjl 		for (i = 0; i < BANKNUM_PER_SB; i++) {
261325cf1a30Sjl 			if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
261425cf1a30Sjl 				restart_patrol(mcp, i, NULL);
261525cf1a30Sjl 			}
261625cf1a30Sjl 		}
261725cf1a30Sjl 	}
261825cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
261925cf1a30Sjl 
262025cf1a30Sjl 	return (DDI_SUCCESS);
262125cf1a30Sjl }
262225cf1a30Sjl 
262325cf1a30Sjl static mc_opl_t *
262425cf1a30Sjl mc_pa_to_mcp(uint64_t pa)
262525cf1a30Sjl {
26260cc8ae86Sav 	mc_opl_t *mcp;
26270cc8ae86Sav 	int i;
26280cc8ae86Sav 
262925cf1a30Sjl 	ASSERT(MUTEX_HELD(&mcmutex));
26300cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
26310cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
26320cc8ae86Sav 			continue;
263325cf1a30Sjl 		/* if mac patrol is suspended, we cannot rely on it */
26340cc8ae86Sav 		if (!(mcp->mc_status & MC_POLL_RUNNING) ||
26350cc8ae86Sav 			(mcp->mc_status & MC_SOFT_SUSPENDED))
263625cf1a30Sjl 			continue;
2637738dd194Shyw 		if (mc_rangecheck_pa(mcp, pa)) {
26380cc8ae86Sav 			return (mcp);
263925cf1a30Sjl 		}
264025cf1a30Sjl 	}
264125cf1a30Sjl 	return (NULL);
264225cf1a30Sjl }
264325cf1a30Sjl 
264425cf1a30Sjl /*
264525cf1a30Sjl  * Get Physical Board number from Logical one.
264625cf1a30Sjl  */
264725cf1a30Sjl static int
264825cf1a30Sjl mc_opl_get_physical_board(int sb)
264925cf1a30Sjl {
265025cf1a30Sjl 	if (&opl_get_physical_board) {
265125cf1a30Sjl 		return (opl_get_physical_board(sb));
265225cf1a30Sjl 	}
265325cf1a30Sjl 
265425cf1a30Sjl 	cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n");
265525cf1a30Sjl 	return (-1);
265625cf1a30Sjl }
265725cf1a30Sjl 
265825cf1a30Sjl /* ARGSUSED */
265925cf1a30Sjl int
266025cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen,
266125cf1a30Sjl 	int *lenp)
266225cf1a30Sjl {
26630cc8ae86Sav 	int i;
2664*aeb241b2Sav 	int j;
266525cf1a30Sjl 	int sb;
26660cc8ae86Sav 	int bank;
2667*aeb241b2Sav 	int cs;
26680cc8ae86Sav 	mc_opl_t *mcp;
26690cc8ae86Sav 	char memb_num;
267025cf1a30Sjl 
267125cf1a30Sjl 	mutex_enter(&mcmutex);
267225cf1a30Sjl 
267325cf1a30Sjl 	if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) ||
267425cf1a30Sjl 		(!pa_is_valid(mcp, flt_addr))) {
267525cf1a30Sjl 		mutex_exit(&mcmutex);
267625cf1a30Sjl 		if (snprintf(buf, buflen, "UNKNOWN") >= buflen) {
267725cf1a30Sjl 			return (ENOSPC);
267825cf1a30Sjl 		} else {
267925cf1a30Sjl 			if (lenp)
268025cf1a30Sjl 				*lenp = strlen(buf);
268125cf1a30Sjl 		}
268225cf1a30Sjl 		return (0);
268325cf1a30Sjl 	}
268425cf1a30Sjl 
268525cf1a30Sjl 	bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address);
2686*aeb241b2Sav 	sb = mcp->mc_phys_board_num;
2687*aeb241b2Sav 	cs = pa_to_cs(mcp, flt_addr - mcp->mc_start_address);
268825cf1a30Sjl 
268925cf1a30Sjl 	if (sb == -1) {
269025cf1a30Sjl 		mutex_exit(&mcmutex);
269125cf1a30Sjl 		return (ENXIO);
269225cf1a30Sjl 	}
269325cf1a30Sjl 
26940cc8ae86Sav 	if (plat_model == MODEL_DC) {
26950cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
2696*aeb241b2Sav 		j = (cs == 0) ? i : i + 2;
2697*aeb241b2Sav 		snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
26980cc8ae86Sav 		    model_names[plat_model].unit_name, sb,
2699*aeb241b2Sav 		    mc_dc_dimm_unum_table[j],
2700*aeb241b2Sav 		    mc_dc_dimm_unum_table[j + 1]);
270125cf1a30Sjl 	} else {
27020cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
2703*aeb241b2Sav 		j = (cs == 0) ? i : i + 2;
27040cc8ae86Sav 		memb_num = mc_ff_dimm_unum_table[i][0];
2705*aeb241b2Sav 		snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s",
27060cc8ae86Sav 		    model_names[plat_model].unit_name,
27070cc8ae86Sav 		    model_names[plat_model].mem_name, memb_num,
2708*aeb241b2Sav 		    &mc_ff_dimm_unum_table[j][1],
2709*aeb241b2Sav 		    &mc_ff_dimm_unum_table[j + 1][1]);
27100cc8ae86Sav 	}
27110cc8ae86Sav 	if (lenp) {
27120cc8ae86Sav 		*lenp = strlen(buf);
271325cf1a30Sjl 	}
271425cf1a30Sjl 	mutex_exit(&mcmutex);
271525cf1a30Sjl 	return (0);
271625cf1a30Sjl }
271725cf1a30Sjl 
271825cf1a30Sjl int
27190cc8ae86Sav opl_mc_suspend(void)
272025cf1a30Sjl {
272125cf1a30Sjl 	mc_opl_t *mcp;
27220cc8ae86Sav 	int i;
272325cf1a30Sjl 
272425cf1a30Sjl 	mutex_enter(&mcmutex);
27250cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
27260cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
27270cc8ae86Sav 			continue;
27280cc8ae86Sav 		mc_suspend(mcp, MC_SOFT_SUSPENDED);
272925cf1a30Sjl 	}
273025cf1a30Sjl 	mutex_exit(&mcmutex);
27310cc8ae86Sav 
273225cf1a30Sjl 	return (0);
273325cf1a30Sjl }
273425cf1a30Sjl 
273525cf1a30Sjl int
27360cc8ae86Sav opl_mc_resume(void)
273725cf1a30Sjl {
273825cf1a30Sjl 	mc_opl_t *mcp;
27390cc8ae86Sav 	int i;
274025cf1a30Sjl 
274125cf1a30Sjl 	mutex_enter(&mcmutex);
27420cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
27430cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
27440cc8ae86Sav 			continue;
27450cc8ae86Sav 		mc_resume(mcp, MC_SOFT_SUSPENDED);
274625cf1a30Sjl 	}
274725cf1a30Sjl 	mutex_exit(&mcmutex);
27480cc8ae86Sav 
274925cf1a30Sjl 	return (0);
275025cf1a30Sjl }
275125cf1a30Sjl static void
275225cf1a30Sjl insert_mcp(mc_opl_t *mcp)
275325cf1a30Sjl {
275425cf1a30Sjl 	mutex_enter(&mcmutex);
27550cc8ae86Sav 	if (mc_instances[mcp->mc_board_num] != NULL) {
27560cc8ae86Sav 		MC_LOG("mc-opl instance for board# %d already exists\n",
27570cc8ae86Sav 			mcp->mc_board_num);
27580cc8ae86Sav 	}
27590cc8ae86Sav 	mc_instances[mcp->mc_board_num] = mcp;
276025cf1a30Sjl 	mutex_exit(&mcmutex);
276125cf1a30Sjl }
276225cf1a30Sjl 
276325cf1a30Sjl static void
276425cf1a30Sjl delete_mcp(mc_opl_t *mcp)
276525cf1a30Sjl {
27660cc8ae86Sav 	mutex_enter(&mcmutex);
27670cc8ae86Sav 	mc_instances[mcp->mc_board_num] = 0;
27680cc8ae86Sav 	mutex_exit(&mcmutex);
276925cf1a30Sjl }
277025cf1a30Sjl 
277125cf1a30Sjl /* Error injection interface */
277225cf1a30Sjl 
2773cfb9e062Shyw static void
2774cfb9e062Shyw mc_lock_va(uint64_t pa, caddr_t new_va)
2775cfb9e062Shyw {
2776cfb9e062Shyw 	tte_t tte;
2777cfb9e062Shyw 
2778738dd194Shyw 	vtag_flushpage(new_va, (uint64_t)ksfmmup);
2779cfb9e062Shyw 	sfmmu_memtte(&tte, pa >> PAGESHIFT,
2780cfb9e062Shyw 		PROC_DATA|HAT_NOSYNC, TTE8K);
2781cfb9e062Shyw 	tte.tte_intlo |= TTE_LCK_INT;
2782cfb9e062Shyw 	sfmmu_dtlb_ld_kva(new_va, &tte);
2783cfb9e062Shyw }
2784cfb9e062Shyw 
2785cfb9e062Shyw static void
2786cfb9e062Shyw mc_unlock_va(caddr_t va)
2787cfb9e062Shyw {
2788cfb9e062Shyw 	vtag_flushpage(va, (uint64_t)ksfmmup);
2789cfb9e062Shyw }
2790cfb9e062Shyw 
279125cf1a30Sjl /* ARGSUSED */
279225cf1a30Sjl int
279325cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags)
279425cf1a30Sjl {
279525cf1a30Sjl 	mc_opl_t *mcp;
279625cf1a30Sjl 	int bank;
279725cf1a30Sjl 	uint32_t dimm_addr;
279825cf1a30Sjl 	uint32_t cntl;
2799738dd194Shyw 	mc_rsaddr_info_t rsaddr;
280025cf1a30Sjl 	uint32_t data, stat;
280125cf1a30Sjl 	int both_sides = 0;
280225cf1a30Sjl 	uint64_t pa0;
2803cfb9e062Shyw 	int extra_injection_needed = 0;
280425cf1a30Sjl 	extern void cpu_flush_ecache(void);
280525cf1a30Sjl 
280625cf1a30Sjl 	MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags);
280725cf1a30Sjl 
280825cf1a30Sjl 	mutex_enter(&mcmutex);
280925cf1a30Sjl 	if ((mcp = mc_pa_to_mcp(pa)) == NULL) {
281025cf1a30Sjl 		mutex_exit(&mcmutex);
281125cf1a30Sjl 		MC_LOG("mc_inject_error: invalid pa\n");
281225cf1a30Sjl 		return (ENOTSUP);
281325cf1a30Sjl 	}
281425cf1a30Sjl 
281525cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
281625cf1a30Sjl 	mutex_exit(&mcmutex);
281725cf1a30Sjl 
281825cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
281925cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
282025cf1a30Sjl 		MC_LOG("mc-opl has been suspended.  No error injection.\n");
282125cf1a30Sjl 		return (EBUSY);
282225cf1a30Sjl 	}
282325cf1a30Sjl 
282425cf1a30Sjl 	/* convert pa to offset within the board */
282525cf1a30Sjl 	MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address);
282625cf1a30Sjl 
282725cf1a30Sjl 	if (!pa_is_valid(mcp, pa)) {
282825cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
282925cf1a30Sjl 		return (EINVAL);
283025cf1a30Sjl 	}
283125cf1a30Sjl 
283225cf1a30Sjl 	pa0 = pa - mcp->mc_start_address;
283325cf1a30Sjl 
283425cf1a30Sjl 	bank = pa_to_bank(mcp, pa0);
283525cf1a30Sjl 
283625cf1a30Sjl 	if (flags & MC_INJECT_FLAG_OTHER)
283725cf1a30Sjl 		bank = bank ^ 1;
283825cf1a30Sjl 
283925cf1a30Sjl 	if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) {
284025cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
284125cf1a30Sjl 		MC_LOG("Not mirror mode\n");
284225cf1a30Sjl 		return (EINVAL);
284325cf1a30Sjl 	}
284425cf1a30Sjl 
284525cf1a30Sjl 	dimm_addr = pa_to_dimm(mcp, pa0);
284625cf1a30Sjl 
2847cfb9e062Shyw 	MC_LOG("injecting error to /LSB%d/B%d/%x\n",
284825cf1a30Sjl 		mcp->mc_board_num, bank, dimm_addr);
284925cf1a30Sjl 
285025cf1a30Sjl 
285125cf1a30Sjl 	switch (error_type) {
285225cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
285325cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
285425cf1a30Sjl 	case MC_INJECT_MUE:
285525cf1a30Sjl 		both_sides = 1;
285625cf1a30Sjl 	}
285725cf1a30Sjl 
285825cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESET)
285925cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0);
286025cf1a30Sjl 
286125cf1a30Sjl 	ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK);
286225cf1a30Sjl 
286325cf1a30Sjl 	if (both_sides) {
286425cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0);
286525cf1a30Sjl 		ST_MAC_REG(MAC_EG_ADD(mcp, bank^1),
286625cf1a30Sjl 			dimm_addr & MAC_EG_ADD_MASK);
286725cf1a30Sjl 	}
286825cf1a30Sjl 
286925cf1a30Sjl 	switch (error_type) {
287025cf1a30Sjl 	case MC_INJECT_SUE:
2871cfb9e062Shyw 		extra_injection_needed = 1;
2872cfb9e062Shyw 		/*FALLTHROUGH*/
2873cfb9e062Shyw 	case MC_INJECT_UE:
287425cf1a30Sjl 	case MC_INJECT_MUE:
287525cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
287625cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
287725cf1a30Sjl 				|MAC_EG_FORCE_READ00|MAC_EG_FORCE_READ16
28780cc8ae86Sav 				|MAC_EG_RDERR_ONCE;
287925cf1a30Sjl 		} else {
288025cf1a30Sjl 			cntl = MAC_EG_ADD_FIX|MAC_EG_FORCE_DERR00
288125cf1a30Sjl 				|MAC_EG_FORCE_DERR16|MAC_EG_DERR_ONCE;
288225cf1a30Sjl 		}
288325cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
288425cf1a30Sjl 		break;
288525cf1a30Sjl 	case MC_INJECT_INTERMITTENT_CE:
288625cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
288725cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
288825cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
288925cf1a30Sjl 				|MAC_EG_FORCE_READ00
28900cc8ae86Sav 				|MAC_EG_RDERR_ONCE;
289125cf1a30Sjl 		} else {
289225cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
289325cf1a30Sjl 				|MAC_EG_FORCE_DERR16
289425cf1a30Sjl 				|MAC_EG_DERR_ONCE;
289525cf1a30Sjl 		}
2896cfb9e062Shyw 		extra_injection_needed = 1;
289725cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
289825cf1a30Sjl 		break;
289925cf1a30Sjl 	case MC_INJECT_PERMANENT_CE:
290025cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
290125cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
290225cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
290325cf1a30Sjl 				|MAC_EG_FORCE_READ00
29040cc8ae86Sav 				|MAC_EG_RDERR_ALWAYS;
290525cf1a30Sjl 		} else {
290625cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
290725cf1a30Sjl 				|MAC_EG_FORCE_DERR16
290825cf1a30Sjl 				|MAC_EG_DERR_ALWAYS;
290925cf1a30Sjl 		}
291025cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
291125cf1a30Sjl 		break;
291225cf1a30Sjl 	case MC_INJECT_CMPE:
291325cf1a30Sjl 		data = 0xabcdefab;
291425cf1a30Sjl 		stphys(pa, data);
291525cf1a30Sjl 		cpu_flush_ecache();
291625cf1a30Sjl 		MC_LOG("CMPE: writing data %x to %lx\n", data, pa);
291725cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE);
291825cf1a30Sjl 		stphys(pa, data ^ 0xffffffff);
2919738dd194Shyw 		membar_sync();
292025cf1a30Sjl 		cpu_flush_ecache();
292125cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), 0);
292225cf1a30Sjl 		MC_LOG("CMPE: write new data %xto %lx\n", data, pa);
292325cf1a30Sjl 		cntl = 0;
292425cf1a30Sjl 		break;
292525cf1a30Sjl 	case MC_INJECT_NOP:
292625cf1a30Sjl 		cntl = 0;
292725cf1a30Sjl 		break;
292825cf1a30Sjl 	default:
292925cf1a30Sjl 		MC_LOG("mc_inject_error: invalid option\n");
293025cf1a30Sjl 		cntl = 0;
293125cf1a30Sjl 	}
293225cf1a30Sjl 
293325cf1a30Sjl 	if (cntl) {
293425cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK);
293525cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
293625cf1a30Sjl 
293725cf1a30Sjl 		if (both_sides) {
293825cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
293925cf1a30Sjl 				MAC_EG_SETUP_MASK);
294025cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
294125cf1a30Sjl 		}
294225cf1a30Sjl 	}
294325cf1a30Sjl 
294425cf1a30Sjl 	/*
294525cf1a30Sjl 	 * For all injection cases except compare error, we
294625cf1a30Sjl 	 * must write to the PA to trigger the error.
294725cf1a30Sjl 	 */
294825cf1a30Sjl 
294925cf1a30Sjl 	if (flags & MC_INJECT_FLAG_ST) {
295025cf1a30Sjl 		data = 0xf0e0d0c0;
295125cf1a30Sjl 		MC_LOG("Writing %x to %lx\n", data, pa);
295225cf1a30Sjl 		stphys(pa, data);
295325cf1a30Sjl 		cpu_flush_ecache();
295425cf1a30Sjl 	}
295525cf1a30Sjl 
295625cf1a30Sjl 
295725cf1a30Sjl 	if (flags & MC_INJECT_FLAG_LD) {
2958cfb9e062Shyw 		if (flags & MC_INJECT_FLAG_PREFETCH) {
2959cfb9e062Shyw 			/*
2960cfb9e062Shyw 			 * Use strong prefetch operation to
2961cfb9e062Shyw 			 * inject MI errors.
2962cfb9e062Shyw 			 */
2963cfb9e062Shyw 			page_t *pp;
2964cfb9e062Shyw 			extern void mc_prefetch(caddr_t);
2965cfb9e062Shyw 
2966cfb9e062Shyw 			MC_LOG("prefetch\n");
2967cfb9e062Shyw 
2968cfb9e062Shyw 			pp = page_numtopp_nolock(pa >> PAGESHIFT);
2969cfb9e062Shyw 			if (pp != NULL) {
2970cfb9e062Shyw 				caddr_t	va, va1;
2971cfb9e062Shyw 
2972cfb9e062Shyw 				va = ppmapin(pp, PROT_READ|PROT_WRITE,
2973cfb9e062Shyw 					(caddr_t)-1);
2974cfb9e062Shyw 				kpreempt_disable();
2975cfb9e062Shyw 				mc_lock_va((uint64_t)pa, va);
2976cfb9e062Shyw 				va1 = va + (pa & (PAGESIZE - 1));
2977cfb9e062Shyw 				mc_prefetch(va1);
2978cfb9e062Shyw 				mc_unlock_va(va);
2979cfb9e062Shyw 				kpreempt_enable();
2980cfb9e062Shyw 				ppmapout(va);
2981cfb9e062Shyw 
2982cfb9e062Shyw 				/*
2983cfb9e062Shyw 				 * For MI errors, we need one extra
2984cfb9e062Shyw 				 * injection for HW patrol to stop.
2985cfb9e062Shyw 				 */
2986cfb9e062Shyw 				extra_injection_needed = 1;
298725cf1a30Sjl 			} else {
2988cfb9e062Shyw 				cmn_err(CE_WARN, "Cannot find page structure"
2989cfb9e062Shyw 					" for PA %lx\n", pa);
299025cf1a30Sjl 			}
299125cf1a30Sjl 		} else {
299225cf1a30Sjl 			MC_LOG("Reading from %lx\n", pa);
299325cf1a30Sjl 			data = ldphys(pa);
299425cf1a30Sjl 			MC_LOG("data = %x\n", data);
299525cf1a30Sjl 		}
2996cfb9e062Shyw 
2997cfb9e062Shyw 		if (extra_injection_needed) {
2998cfb9e062Shyw 			/*
2999cfb9e062Shyw 			 * These are the injection cases where the
3000cfb9e062Shyw 			 * requested injected errors will not cause the HW
3001cfb9e062Shyw 			 * patrol to stop. For these cases, we need to inject
3002cfb9e062Shyw 			 * an extra 'real' PTRL error to force the
3003cfb9e062Shyw 			 * HW patrol to stop so that we can report the
3004cfb9e062Shyw 			 * errors injected. Note that we cannot read
3005cfb9e062Shyw 			 * and report error status while the HW patrol
3006cfb9e062Shyw 			 * is running.
3007cfb9e062Shyw 			 */
3008cfb9e062Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank),
3009cfb9e062Shyw 				cntl & MAC_EG_SETUP_MASK);
3010cfb9e062Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
3011cfb9e062Shyw 
3012cfb9e062Shyw 			if (both_sides) {
3013cfb9e062Shyw 			    ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
3014cfb9e062Shyw 				MAC_EG_SETUP_MASK);
3015cfb9e062Shyw 			    ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
3016cfb9e062Shyw 			}
3017cfb9e062Shyw 			data = 0xf0e0d0c0;
3018cfb9e062Shyw 			MC_LOG("Writing %x to %lx\n", data, pa);
3019cfb9e062Shyw 			stphys(pa, data);
3020cfb9e062Shyw 			cpu_flush_ecache();
3021cfb9e062Shyw 		}
302225cf1a30Sjl 	}
302325cf1a30Sjl 
302425cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESTART) {
302525cf1a30Sjl 		MC_LOG("Restart patrol\n");
3026738dd194Shyw 		rsaddr.mi_restartaddr.ma_bd = mcp->mc_board_num;
3027738dd194Shyw 		rsaddr.mi_restartaddr.ma_bank = bank;
3028738dd194Shyw 		rsaddr.mi_restartaddr.ma_dimm_addr = dimm_addr;
3029738dd194Shyw 		rsaddr.mi_valid = 1;
3030738dd194Shyw 		rsaddr.mi_injectrestart = 1;
3031738dd194Shyw 		restart_patrol(mcp, bank, &rsaddr);
303225cf1a30Sjl 	}
303325cf1a30Sjl 
303425cf1a30Sjl 	if (flags & MC_INJECT_FLAG_POLL) {
30350cc8ae86Sav 		int running;
303637afe445Shyw 		int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
303725cf1a30Sjl 
303825cf1a30Sjl 		MC_LOG("Poll patrol error\n");
303925cf1a30Sjl 		stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank));
304025cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
30410cc8ae86Sav 		running = cntl & MAC_CNTL_PTRL_START;
304225cf1a30Sjl 
3043cfb9e062Shyw 		if (!running &&
3044cfb9e062Shyw 		    (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS))) {
3045cfb9e062Shyw 			/*
3046cfb9e062Shyw 			 * HW patrol stopped and we have errors to
3047cfb9e062Shyw 			 * report. Do it.
3048cfb9e062Shyw 			 */
304937afe445Shyw 			mcp->mc_speedup_period[ebank] = 0;
3050738dd194Shyw 			rsaddr.mi_valid = 0;
3051738dd194Shyw 			rsaddr.mi_injectrestart = 0;
3052738dd194Shyw 			if (IS_MIRROR(mcp, bank)) {
3053738dd194Shyw 				mc_error_handler_mir(mcp, bank, &rsaddr);
3054738dd194Shyw 			} else {
3055738dd194Shyw 				mc_error_handler(mcp, bank, &rsaddr);
3056738dd194Shyw 			}
3057cfb9e062Shyw 
3058738dd194Shyw 			restart_patrol(mcp, bank, &rsaddr);
3059cfb9e062Shyw 		} else {
3060cfb9e062Shyw 			/*
3061cfb9e062Shyw 			 * We are expecting to report injected
3062cfb9e062Shyw 			 * errors but the HW patrol is still running.
3063cfb9e062Shyw 			 * Speed up the scanning
3064cfb9e062Shyw 			 */
306537afe445Shyw 			mcp->mc_speedup_period[ebank] = 2;
3066cfb9e062Shyw 			MAC_CMD(mcp, bank, 0);
306725cf1a30Sjl 			restart_patrol(mcp, bank, NULL);
3068cfb9e062Shyw 		}
306925cf1a30Sjl 	}
307025cf1a30Sjl 
307125cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
307225cf1a30Sjl 	return (0);
307325cf1a30Sjl }
3074cfb9e062Shyw 
307525cf1a30Sjl void
307625cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data)
307725cf1a30Sjl {
307825cf1a30Sjl 	MC_LOG("0x%x -> pa(%lx)\n", data, pa);
307925cf1a30Sjl 	stphysio(pa, data);
30800cc8ae86Sav 
30810cc8ae86Sav 	/* force the above write to be processed by mac patrol */
3082cfb9e062Shyw 	data = ldphysio(pa);
3083cfb9e062Shyw 	MC_LOG("pa(%lx) = 0x%x\n", pa, data);
308425cf1a30Sjl }
308525cf1a30Sjl 
308625cf1a30Sjl uint32_t
308725cf1a30Sjl mc_ldphysio(uint64_t pa)
308825cf1a30Sjl {
308925cf1a30Sjl 	uint32_t rv;
309025cf1a30Sjl 
309125cf1a30Sjl 	rv = ldphysio(pa);
309225cf1a30Sjl 	MC_LOG("pa(%lx) = 0x%x\n", pa, rv);
309325cf1a30Sjl 	return (rv);
309425cf1a30Sjl }
30950cc8ae86Sav 
30960cc8ae86Sav #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
30970cc8ae86Sav 
30980cc8ae86Sav /*
30990cc8ae86Sav  * parse_unum_memory -- extract the board number and the DIMM name from
31000cc8ae86Sav  * the unum.
31010cc8ae86Sav  *
31020cc8ae86Sav  * Return 0 for success and non-zero for a failure.
31030cc8ae86Sav  */
31040cc8ae86Sav int
31050cc8ae86Sav parse_unum_memory(char *unum, int *board, char *dname)
31060cc8ae86Sav {
31070cc8ae86Sav 	char *c;
31080cc8ae86Sav 	char x, y, z;
31090cc8ae86Sav 
31100cc8ae86Sav 	if ((c = strstr(unum, "CMU")) != NULL) {
31110cc8ae86Sav 		/* DC Model */
31120cc8ae86Sav 		c += 3;
31130cc8ae86Sav 		*board = (uint8_t)stoi(&c);
31140cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
31150cc8ae86Sav 			return (1);
31160cc8ae86Sav 		}
31170cc8ae86Sav 		c += 3;
31180cc8ae86Sav 		if (strlen(c) < 3) {
31190cc8ae86Sav 			return (2);
31200cc8ae86Sav 		}
31210cc8ae86Sav 		if ((!isdigit(c[0])) || (!(isdigit(c[1]))) ||
31220cc8ae86Sav 		    ((c[2] != 'A') && (c[2] != 'B'))) {
31230cc8ae86Sav 			return (3);
31240cc8ae86Sav 		}
31250cc8ae86Sav 		x = c[0];
31260cc8ae86Sav 		y = c[1];
31270cc8ae86Sav 		z = c[2];
31280cc8ae86Sav 	} else if ((c = strstr(unum, "MBU_")) != NULL) {
31290cc8ae86Sav 		/*  FF1/FF2 Model */
31300cc8ae86Sav 		c += 4;
31310cc8ae86Sav 		if ((c[0] != 'A') && (c[0] != 'B')) {
31320cc8ae86Sav 			return (4);
31330cc8ae86Sav 		}
31340cc8ae86Sav 		if ((c = strstr(c, "MEMB")) == NULL) {
31350cc8ae86Sav 			return (5);
31360cc8ae86Sav 		}
31370cc8ae86Sav 		c += 4;
31380cc8ae86Sav 
31390cc8ae86Sav 		x = c[0];
31400cc8ae86Sav 		*board =  ((uint8_t)stoi(&c)) / 4;
31410cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
31420cc8ae86Sav 			return (6);
31430cc8ae86Sav 		}
31440cc8ae86Sav 		c += 3;
31450cc8ae86Sav 		if (strlen(c) < 2) {
31460cc8ae86Sav 			return (7);
31470cc8ae86Sav 		}
31480cc8ae86Sav 		if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) {
31490cc8ae86Sav 			return (8);
31500cc8ae86Sav 		}
31510cc8ae86Sav 		y = c[0];
31520cc8ae86Sav 		z = c[1];
31530cc8ae86Sav 	} else {
31540cc8ae86Sav 		return (9);
31550cc8ae86Sav 	}
31560cc8ae86Sav 	if (*board < 0) {
31570cc8ae86Sav 		return (10);
31580cc8ae86Sav 	}
31590cc8ae86Sav 	dname[0] = x;
31600cc8ae86Sav 	dname[1] = y;
31610cc8ae86Sav 	dname[2] = z;
31620cc8ae86Sav 	dname[3] = '\0';
31630cc8ae86Sav 	return (0);
31640cc8ae86Sav }
31650cc8ae86Sav 
31660cc8ae86Sav /*
31670cc8ae86Sav  * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and
31680cc8ae86Sav  * the DIMM name.
31690cc8ae86Sav  */
31700cc8ae86Sav int
31710cc8ae86Sav mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
31720cc8ae86Sav     int buflen, int *lenp)
31730cc8ae86Sav {
31740cc8ae86Sav 	int		ret = ENODEV;
31750cc8ae86Sav 	mc_dimm_info_t	*d = NULL;
31760cc8ae86Sav 
31770cc8ae86Sav 	if ((d = mcp->mc_dimm_list) == NULL)
31780cc8ae86Sav 		return (ENOTSUP);
31790cc8ae86Sav 
31800cc8ae86Sav 	for (; d != NULL; d = d->md_next) {
31810cc8ae86Sav 		if (strcmp(d->md_dimmname, dname) == 0) {
31820cc8ae86Sav 			break;
31830cc8ae86Sav 		}
31840cc8ae86Sav 	}
31850cc8ae86Sav 	if (d != NULL) {
31860cc8ae86Sav 		*lenp = strlen(d->md_serial) + strlen(d->md_partnum);
31870cc8ae86Sav 		if (buflen <=  *lenp) {
31880cc8ae86Sav 			cmn_err(CE_WARN, "mc_get_mem_sid_dimm: "
31890cc8ae86Sav 			    "buflen is smaller than %d\n", *lenp);
31900cc8ae86Sav 			ret = ENOSPC;
31910cc8ae86Sav 		} else {
31920cc8ae86Sav 			snprintf(buf, buflen, "%s:%s",
31930cc8ae86Sav 			    d->md_serial, d->md_partnum);
31940cc8ae86Sav 			ret = 0;
31950cc8ae86Sav 		}
31960cc8ae86Sav 	}
31970cc8ae86Sav 	MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n",
31980cc8ae86Sav 	    ret, dname, (ret == 0) ? buf : "");
31990cc8ae86Sav 	return (ret);
32000cc8ae86Sav }
32010cc8ae86Sav 
32020cc8ae86Sav int
3203*aeb241b2Sav mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int sb,
32040cc8ae86Sav     int bank, uint32_t mf_type, uint32_t d_slot)
32050cc8ae86Sav {
32060cc8ae86Sav 	int	lenp = buflen;
32070cc8ae86Sav 	int	id;
32080cc8ae86Sav 	int	ret;
32090cc8ae86Sav 	char	*dimmnm;
32100cc8ae86Sav 
32110cc8ae86Sav 	if (mf_type == FLT_TYPE_PERMANENT_CE) {
32120cc8ae86Sav 		if (plat_model == MODEL_DC) {
32130cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
3214*aeb241b2Sav 			dimmnm = mc_dc_dimm_unum_table[id];
32150cc8ae86Sav 		} else {
32160cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
3217*aeb241b2Sav 			dimmnm = mc_ff_dimm_unum_table[id];
32180cc8ae86Sav 		}
32190cc8ae86Sav 		if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen,
32200cc8ae86Sav 		    &lenp)) != 0) {
32210cc8ae86Sav 			return (ret);
32220cc8ae86Sav 		}
32230cc8ae86Sav 	} else {
32240cc8ae86Sav 		return (1);
32250cc8ae86Sav 	}
32260cc8ae86Sav 
32270cc8ae86Sav 	return (0);
32280cc8ae86Sav }
32290cc8ae86Sav 
32300cc8ae86Sav /*
32310cc8ae86Sav  * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum.
32320cc8ae86Sav  */
32330cc8ae86Sav int
32340cc8ae86Sav mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
32350cc8ae86Sav {
32360cc8ae86Sav 	int	i;
32370cc8ae86Sav 	int	ret = ENODEV;
32380cc8ae86Sav 	int	board;
32390cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
32400cc8ae86Sav 	mc_opl_t *mcp;
32410cc8ae86Sav 
32420cc8ae86Sav 	MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen);
32430cc8ae86Sav 	if ((ret = parse_unum_memory(unum, &board, dname)) != 0) {
32440cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
32450cc8ae86Sav 		    unum, ret);
32460cc8ae86Sav 		return (EINVAL);
32470cc8ae86Sav 	}
32480cc8ae86Sav 
32490cc8ae86Sav 	if (board < 0) {
32500cc8ae86Sav 		MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n",
32510cc8ae86Sav 		    board, dname);
32520cc8ae86Sav 		return (EINVAL);
32530cc8ae86Sav 	}
32540cc8ae86Sav 
32550cc8ae86Sav 	mutex_enter(&mcmutex);
32560cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
32570cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
32580cc8ae86Sav 			continue;
32590cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3260*aeb241b2Sav 		if (mcp->mc_phys_board_num != board) {
3261*aeb241b2Sav 			mutex_exit(&mcp->mc_lock);
3262*aeb241b2Sav 			continue;
3263*aeb241b2Sav 		}
3264*aeb241b2Sav 		ret = mc_get_mem_sid_dimm(mcp, dname, buf, buflen, lenp);
3265*aeb241b2Sav 		if (ret == 0) {
32660cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
32670cc8ae86Sav 			break;
32680cc8ae86Sav 		}
32690cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
32700cc8ae86Sav 	}
32710cc8ae86Sav 	mutex_exit(&mcmutex);
32720cc8ae86Sav 	return (ret);
32730cc8ae86Sav }
32740cc8ae86Sav 
32750cc8ae86Sav /*
32760cc8ae86Sav  * mc_get_mem_offset -- get the offset in a DIMM for a given physical address.
32770cc8ae86Sav  */
32780cc8ae86Sav int
32790cc8ae86Sav mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
32800cc8ae86Sav {
32810cc8ae86Sav 	int		i;
32820cc8ae86Sav 	int		ret = ENODEV;
32830cc8ae86Sav 	mc_addr_t	maddr;
32840cc8ae86Sav 	mc_opl_t	*mcp;
32850cc8ae86Sav 
32860cc8ae86Sav 	mutex_enter(&mcmutex);
3287c964b0e6Sraghuram 	for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) {
32880cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
32890cc8ae86Sav 			continue;
32900cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
32910cc8ae86Sav 		if (!pa_is_valid(mcp, paddr)) {
32920cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
32930cc8ae86Sav 			continue;
32940cc8ae86Sav 		}
32950cc8ae86Sav 		if (pa_to_maddr(mcp, paddr, &maddr) == 0) {
32960cc8ae86Sav 			*offp = maddr.ma_dimm_addr;
32970cc8ae86Sav 			ret = 0;
32980cc8ae86Sav 		}
32990cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
33000cc8ae86Sav 	}
33010cc8ae86Sav 	mutex_exit(&mcmutex);
33020cc8ae86Sav 	MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n",
33030cc8ae86Sav 	    ret, paddr, *offp);
33040cc8ae86Sav 	return (ret);
33050cc8ae86Sav }
33060cc8ae86Sav 
33070cc8ae86Sav /*
33080cc8ae86Sav  * dname_to_bankslot - Get the bank and slot number from the DIMM name.
33090cc8ae86Sav  */
33100cc8ae86Sav int
33110cc8ae86Sav dname_to_bankslot(char *dname, int *bank, int *slot)
33120cc8ae86Sav {
33130cc8ae86Sav 	int i;
33140cc8ae86Sav 	int tsz;
33150cc8ae86Sav 	char **tbl;
33160cc8ae86Sav 
33170cc8ae86Sav 	if (plat_model == MODEL_DC) { /* DC */
33180cc8ae86Sav 		tbl = mc_dc_dimm_unum_table;
33190cc8ae86Sav 		tsz = OPL_MAX_DIMMS;
33200cc8ae86Sav 	} else {
33210cc8ae86Sav 		tbl = mc_ff_dimm_unum_table;
33220cc8ae86Sav 		tsz = 2 * OPL_MAX_DIMMS;
33230cc8ae86Sav 	}
33240cc8ae86Sav 
33250cc8ae86Sav 	for (i = 0; i < tsz; i++) {
33260cc8ae86Sav 		if (strcmp(dname,  tbl[i]) == 0) {
33270cc8ae86Sav 			break;
33280cc8ae86Sav 		}
33290cc8ae86Sav 	}
33300cc8ae86Sav 	if (i == tsz) {
33310cc8ae86Sav 		return (1);
33320cc8ae86Sav 	}
33330cc8ae86Sav 	*bank = INDEX_TO_BANK(i);
33340cc8ae86Sav 	*slot = INDEX_TO_SLOT(i);
33350cc8ae86Sav 	return (0);
33360cc8ae86Sav }
33370cc8ae86Sav 
33380cc8ae86Sav /*
33390cc8ae86Sav  * mc_get_mem_addr -- get the physical address of a DIMM corresponding
33400cc8ae86Sav  * to the unum and sid.
33410cc8ae86Sav  */
33420cc8ae86Sav int
33430cc8ae86Sav mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr)
33440cc8ae86Sav {
33450cc8ae86Sav 	int	board;
33460cc8ae86Sav 	int	bank;
33470cc8ae86Sav 	int	slot;
33480cc8ae86Sav 	int	i;
33490cc8ae86Sav 	int	ret = ENODEV;
33500cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
33510cc8ae86Sav 	mc_addr_t maddr;
33520cc8ae86Sav 	mc_opl_t *mcp;
33530cc8ae86Sav 
33540cc8ae86Sav 	MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n",
33550cc8ae86Sav 	    unum, sid, offset);
33560cc8ae86Sav 	if (parse_unum_memory(unum, &board, dname) != 0) {
33570cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
33580cc8ae86Sav 		    unum, ret);
33590cc8ae86Sav 		return (EINVAL);
33600cc8ae86Sav 	}
33610cc8ae86Sav 
33620cc8ae86Sav 	if (board < 0) {
33630cc8ae86Sav 		MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n",
33640cc8ae86Sav 		    board, dname);
33650cc8ae86Sav 		return (EINVAL);
33660cc8ae86Sav 	}
33670cc8ae86Sav 
33680cc8ae86Sav 	mutex_enter(&mcmutex);
33690cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
33700cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
33710cc8ae86Sav 			continue;
33720cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3373*aeb241b2Sav 		if (mcp->mc_phys_board_num != board) {
33740cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
33750cc8ae86Sav 			continue;
33760cc8ae86Sav 		}
33770cc8ae86Sav 
33780cc8ae86Sav 		ret = dname_to_bankslot(dname, &bank, &slot);
33790cc8ae86Sav 		MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot);
33800cc8ae86Sav 		if (ret != 0) {
33810cc8ae86Sav 			MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n");
33820cc8ae86Sav 			ret = ENODEV;
33830cc8ae86Sav 		} else {
3384*aeb241b2Sav 			maddr.ma_bd = mcp->mc_board_num;
33850cc8ae86Sav 			maddr.ma_bank =  bank;
33860cc8ae86Sav 			maddr.ma_dimm_addr = offset;
33870cc8ae86Sav 			ret = mcaddr_to_pa(mcp, &maddr, paddr);
33880cc8ae86Sav 			if (ret != 0) {
33890cc8ae86Sav 				MC_LOG("mc_get_mem_addr: "
33900cc8ae86Sav 				    "mcaddr_to_pa failed\n");
33910cc8ae86Sav 				ret = ENODEV;
33920cc8ae86Sav 			}
3393*aeb241b2Sav 			mutex_exit(&mcp->mc_lock);
3394*aeb241b2Sav 			break;
33950cc8ae86Sav 		}
33960cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
33970cc8ae86Sav 	}
33980cc8ae86Sav 	mutex_exit(&mcmutex);
33990cc8ae86Sav 	MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr);
34000cc8ae86Sav 	return (ret);
34010cc8ae86Sav }
34020cc8ae86Sav 
34030cc8ae86Sav static void
34040cc8ae86Sav mc_free_dimm_list(mc_dimm_info_t *d)
34050cc8ae86Sav {
34060cc8ae86Sav 	mc_dimm_info_t *next;
34070cc8ae86Sav 
34080cc8ae86Sav 	while (d != NULL) {
34090cc8ae86Sav 		next = d->md_next;
34100cc8ae86Sav 		kmem_free(d, sizeof (mc_dimm_info_t));
34110cc8ae86Sav 		d = next;
34120cc8ae86Sav 	}
34130cc8ae86Sav }
34140cc8ae86Sav 
34150cc8ae86Sav /*
34160cc8ae86Sav  * mc_get_dimm_list -- get the list of dimms with serial-id info
34170cc8ae86Sav  * from the SP.
34180cc8ae86Sav  */
34190cc8ae86Sav mc_dimm_info_t *
34200cc8ae86Sav mc_get_dimm_list(mc_opl_t *mcp)
34210cc8ae86Sav {
34220cc8ae86Sav 	uint32_t	bufsz;
34230cc8ae86Sav 	uint32_t	maxbufsz;
34240cc8ae86Sav 	int		ret;
34250cc8ae86Sav 	int		sexp;
34260cc8ae86Sav 	board_dimm_info_t *bd_dimmp;
34270cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
34280cc8ae86Sav 
34290cc8ae86Sav 	maxbufsz = bufsz = sizeof (board_dimm_info_t) +
34300cc8ae86Sav 	    ((MCOPL_MAX_DIMMNAME +  MCOPL_MAX_SERIAL +
34310cc8ae86Sav 	    MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS);
34320cc8ae86Sav 
34330cc8ae86Sav 	bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP);
34340cc8ae86Sav 	ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz);
34350cc8ae86Sav 
34360cc8ae86Sav 	MC_LOG("mc_get_dimm_list:  scf_service_getinfo returned=%d\n", ret);
34370cc8ae86Sav 	if (ret == 0) {
34380cc8ae86Sav 		sexp = sizeof (board_dimm_info_t) +
34390cc8ae86Sav 		    ((bd_dimmp->bd_dnamesz +  bd_dimmp->bd_serialsz +
34400cc8ae86Sav 		    bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms);
34410cc8ae86Sav 
34420cc8ae86Sav 		if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) &&
34430cc8ae86Sav 		    (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) &&
34440cc8ae86Sav 		    (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) &&
34450cc8ae86Sav 		    (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) &&
34460cc8ae86Sav 		    (sexp <= bufsz)) {
34470cc8ae86Sav 
34480cc8ae86Sav #ifdef DEBUG
34490cc8ae86Sav 			if (oplmc_debug)
34500cc8ae86Sav 				mc_dump_dimm_info(bd_dimmp);
34510cc8ae86Sav #endif
34520cc8ae86Sav 			dimm_list = mc_prepare_dimmlist(bd_dimmp);
34530cc8ae86Sav 
34540cc8ae86Sav 		} else {
34550cc8ae86Sav 			cmn_err(CE_WARN, "DIMM info version mismatch\n");
34560cc8ae86Sav 		}
34570cc8ae86Sav 	}
34580cc8ae86Sav 	kmem_free(bd_dimmp, maxbufsz);
34590cc8ae86Sav 	MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list);
34600cc8ae86Sav 	return (dimm_list);
34610cc8ae86Sav }
34620cc8ae86Sav 
34630cc8ae86Sav /*
34640cc8ae86Sav  * mc_prepare_dimmlist - Prepare the dimm list from the infomation
34650cc8ae86Sav  * recieved from the SP.
34660cc8ae86Sav  */
34670cc8ae86Sav mc_dimm_info_t *
34680cc8ae86Sav mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp)
34690cc8ae86Sav {
34700cc8ae86Sav 	char	*dimm_name;
34710cc8ae86Sav 	char	*serial;
34720cc8ae86Sav 	char	*part;
34730cc8ae86Sav 	int	dimm;
34740cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
34750cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
34760cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
34770cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
34780cc8ae86Sav 	mc_dimm_info_t	*d;
34790cc8ae86Sav 
34800cc8ae86Sav 	dimm_name = (char *)(bd_dimmp + 1);
34810cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
34820cc8ae86Sav 
34830cc8ae86Sav 		d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t),
34840cc8ae86Sav 		    KM_SLEEP);
34850cc8ae86Sav 		snprintf(d->md_dimmname, dnamesz + 1, "%s", dimm_name);
34860cc8ae86Sav 		serial = dimm_name + dnamesz;
34870cc8ae86Sav 		snprintf(d->md_serial, sersz + 1, "%s", serial);
34880cc8ae86Sav 		part = serial + sersz;
34890cc8ae86Sav 		snprintf(d->md_partnum, partsz + 1, "%s", part);
34900cc8ae86Sav 
34910cc8ae86Sav 		d->md_next = dimm_list;
34920cc8ae86Sav 		dimm_list = d;
34930cc8ae86Sav 		dimm_name = part + partsz;
34940cc8ae86Sav 	}
34950cc8ae86Sav 	return (dimm_list);
34960cc8ae86Sav }
34970cc8ae86Sav 
34980cc8ae86Sav #ifdef DEBUG
34990cc8ae86Sav void
35000cc8ae86Sav mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz)
35010cc8ae86Sav {
35020cc8ae86Sav 	char dname[MCOPL_MAX_DIMMNAME + 1];
35030cc8ae86Sav 	char serial[MCOPL_MAX_SERIAL + 1];
35040cc8ae86Sav 	char part[ MCOPL_MAX_PARTNUM + 1];
35050cc8ae86Sav 	char *b;
35060cc8ae86Sav 
35070cc8ae86Sav 	b = buf;
35080cc8ae86Sav 	snprintf(dname, dnamesz + 1, "%s", b);
35090cc8ae86Sav 	b += dnamesz;
35100cc8ae86Sav 	snprintf(serial, serialsz + 1, "%s", b);
35110cc8ae86Sav 	b += serialsz;
35120cc8ae86Sav 	snprintf(part, partnumsz + 1, "%s", b);
35130cc8ae86Sav 	printf("DIMM=%s  Serial=%s PartNum=%s\n", dname, serial, part);
35140cc8ae86Sav }
35150cc8ae86Sav 
35160cc8ae86Sav void
35170cc8ae86Sav mc_dump_dimm_info(board_dimm_info_t *bd_dimmp)
35180cc8ae86Sav {
35190cc8ae86Sav 	int	dimm;
35200cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
35210cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
35220cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
35230cc8ae86Sav 	char	*buf;
35240cc8ae86Sav 
35250cc8ae86Sav 	printf("Version=%d Board=%02d DIMMs=%d NameSize=%d "
35260cc8ae86Sav 	    "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version,
35270cc8ae86Sav 	    bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz,
35280cc8ae86Sav 	    bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz);
35290cc8ae86Sav 	printf("======================================================\n");
35300cc8ae86Sav 
35310cc8ae86Sav 	buf = (char *)(bd_dimmp + 1);
35320cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
35330cc8ae86Sav 		mc_dump_dimm(buf, dnamesz, sersz, partsz);
35340cc8ae86Sav 		buf += dnamesz + sersz + partsz;
35350cc8ae86Sav 	}
35360cc8ae86Sav 	printf("======================================================\n");
35370cc8ae86Sav }
35380cc8ae86Sav 
35390cc8ae86Sav 
35400cc8ae86Sav /* ARGSUSED */
35410cc8ae86Sav static int
35420cc8ae86Sav mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
35430cc8ae86Sav 	int *rvalp)
35440cc8ae86Sav {
35450cc8ae86Sav 	caddr_t	buf;
35460cc8ae86Sav 	uint64_t pa;
35470cc8ae86Sav 	int rv = 0;
35480cc8ae86Sav 	int i;
35490cc8ae86Sav 	uint32_t flags;
35500cc8ae86Sav 	static uint32_t offset = 0;
35510cc8ae86Sav 
35520cc8ae86Sav 
35530cc8ae86Sav 	flags = (cmd >> 4) & 0xfffffff;
35540cc8ae86Sav 
35550cc8ae86Sav 	cmd &= 0xf;
35560cc8ae86Sav 
35570cc8ae86Sav 	MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags);
35580cc8ae86Sav 
35590cc8ae86Sav 	if (arg != NULL) {
35600cc8ae86Sav 		if (ddi_copyin((const void *)arg, (void *)&pa,
35610cc8ae86Sav 			sizeof (uint64_t), 0) < 0) {
35620cc8ae86Sav 			rv = EFAULT;
35630cc8ae86Sav 			return (rv);
35640cc8ae86Sav 		}
35650cc8ae86Sav 		buf = NULL;
35660cc8ae86Sav 	} else {
35670cc8ae86Sav 		buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP);
35680cc8ae86Sav 
35690cc8ae86Sav 		pa = va_to_pa(buf);
35700cc8ae86Sav 		pa += offset;
35710cc8ae86Sav 
35720cc8ae86Sav 		offset += 64;
35730cc8ae86Sav 		if (offset >= PAGESIZE)
35740cc8ae86Sav 			offset = 0;
35750cc8ae86Sav 	}
35760cc8ae86Sav 
35770cc8ae86Sav 	switch (cmd) {
35780cc8ae86Sav 	case MCI_CE:
35790cc8ae86Sav 		mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa,
35800cc8ae86Sav 			flags);
35810cc8ae86Sav 		break;
35820cc8ae86Sav 	case MCI_PERM_CE:
35830cc8ae86Sav 		mc_inject_error(MC_INJECT_PERMANENT_CE, pa,
35840cc8ae86Sav 			flags);
35850cc8ae86Sav 		break;
35860cc8ae86Sav 	case MCI_UE:
35870cc8ae86Sav 		mc_inject_error(MC_INJECT_UE, pa,
35880cc8ae86Sav 			flags);
35890cc8ae86Sav 		break;
35900cc8ae86Sav 	case MCI_M_CE:
35910cc8ae86Sav 		mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa,
35920cc8ae86Sav 			flags);
35930cc8ae86Sav 		break;
35940cc8ae86Sav 	case MCI_M_PCE:
35950cc8ae86Sav 		mc_inject_error(MC_INJECT_PERMANENT_MCE, pa,
35960cc8ae86Sav 			flags);
35970cc8ae86Sav 		break;
35980cc8ae86Sav 	case MCI_M_UE:
35990cc8ae86Sav 		mc_inject_error(MC_INJECT_MUE, pa,
36000cc8ae86Sav 			flags);
36010cc8ae86Sav 		break;
36020cc8ae86Sav 	case MCI_CMP:
36030cc8ae86Sav 		mc_inject_error(MC_INJECT_CMPE, pa,
36040cc8ae86Sav 			flags);
36050cc8ae86Sav 		break;
36060cc8ae86Sav 	case MCI_NOP:
36070cc8ae86Sav 		mc_inject_error(MC_INJECT_NOP, pa, flags);
36080cc8ae86Sav 		break;
36090cc8ae86Sav 	case MCI_SHOW_ALL:
36100cc8ae86Sav 		mc_debug_show_all = 1;
36110cc8ae86Sav 		break;
36120cc8ae86Sav 	case MCI_SHOW_NONE:
36130cc8ae86Sav 		mc_debug_show_all = 0;
36140cc8ae86Sav 		break;
36150cc8ae86Sav 	case MCI_ALLOC:
36160cc8ae86Sav 		/*
36170cc8ae86Sav 		 * just allocate some kernel memory and never free it
36180cc8ae86Sav 		 * 512 MB seems to be the maximum size supported.
36190cc8ae86Sav 		 */
36200cc8ae86Sav 		cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512);
36210cc8ae86Sav 		for (i = 0; i < flags; i++) {
36220cc8ae86Sav 			buf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP);
36230cc8ae86Sav 			cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n",
36240cc8ae86Sav 				(u_longlong_t)buf, (u_longlong_t)va_to_pa(buf));
36250cc8ae86Sav 		}
36260cc8ae86Sav 		break;
36270cc8ae86Sav 	case MCI_SUSPEND:
36280cc8ae86Sav 		(void) opl_mc_suspend();
36290cc8ae86Sav 		break;
36300cc8ae86Sav 	case MCI_RESUME:
36310cc8ae86Sav 		(void) opl_mc_resume();
36320cc8ae86Sav 		break;
36330cc8ae86Sav 	default:
36340cc8ae86Sav 		rv = ENXIO;
36350cc8ae86Sav 	}
36360cc8ae86Sav 	return (rv);
36370cc8ae86Sav }
36380cc8ae86Sav 
36390cc8ae86Sav #endif /* DEBUG */
3640