xref: /onnv-gate/usr/src/uts/sun4u/opl/io/mc-opl.c (revision 6693:944dc748ba76)
11772Sjl139090 /*
21772Sjl139090  * CDDL HEADER START
31772Sjl139090  *
41772Sjl139090  * The contents of this file are subject to the terms of the
51772Sjl139090  * Common Development and Distribution License (the "License").
61772Sjl139090  * You may not use this file except in compliance with the License.
71772Sjl139090  *
81772Sjl139090  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91772Sjl139090  * or http://www.opensolaris.org/os/licensing.
101772Sjl139090  * See the License for the specific language governing permissions
111772Sjl139090  * and limitations under the License.
121772Sjl139090  *
131772Sjl139090  * When distributing Covered Code, include this CDDL HEADER in each
141772Sjl139090  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151772Sjl139090  * If applicable, add the following below this CDDL HEADER, with the
161772Sjl139090  * fields enclosed by brackets "[]" replaced with your own identifying
171772Sjl139090  * information: Portions Copyright [yyyy] [name of copyright owner]
181772Sjl139090  *
191772Sjl139090  * CDDL HEADER END
201772Sjl139090  */
211772Sjl139090 /*
225979Sjimand  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
232241Shuah  * Use is subject to license terms.
242241Shuah  */
252241Shuah /*
266297Sjl139090  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2008
271772Sjl139090  */
281772Sjl139090 
291772Sjl139090 #pragma ident	"%Z%%M%	%I%	%E% SMI"
301772Sjl139090 
311772Sjl139090 #include <sys/types.h>
321772Sjl139090 #include <sys/sysmacros.h>
331772Sjl139090 #include <sys/conf.h>
341772Sjl139090 #include <sys/modctl.h>
351772Sjl139090 #include <sys/stat.h>
361772Sjl139090 #include <sys/async.h>
372241Shuah #include <sys/machcpuvar.h>
381772Sjl139090 #include <sys/machsystm.h>
392214Sav145390 #include <sys/promif.h>
401772Sjl139090 #include <sys/ksynch.h>
411772Sjl139090 #include <sys/ddi.h>
421772Sjl139090 #include <sys/sunddi.h>
435080Swh31274 #include <sys/sunndi.h>
441772Sjl139090 #include <sys/ddifm.h>
451772Sjl139090 #include <sys/fm/protocol.h>
461772Sjl139090 #include <sys/fm/util.h>
471772Sjl139090 #include <sys/kmem.h>
481772Sjl139090 #include <sys/fm/io/opl_mc_fm.h>
491772Sjl139090 #include <sys/memlist.h>
501772Sjl139090 #include <sys/param.h>
512214Sav145390 #include <sys/disp.h>
521772Sjl139090 #include <vm/page.h>
531772Sjl139090 #include <sys/mc-opl.h>
542214Sav145390 #include <sys/opl.h>
552214Sav145390 #include <sys/opl_dimm.h>
562214Sav145390 #include <sys/scfd/scfostoescf.h>
572494Shyw #include <sys/cpu_module.h>
582494Shyw #include <vm/seg_kmem.h>
592494Shyw #include <sys/vmem.h>
602494Shyw #include <vm/hat_sfmmu.h>
612494Shyw #include <sys/vmsystm.h>
622662Shyw #include <sys/membar.h>
63*6693Swh31274 #include <sys/mem.h>
641772Sjl139090 
651772Sjl139090 /*
661772Sjl139090  * Function prototypes
671772Sjl139090  */
681772Sjl139090 static int mc_open(dev_t *, int, int, cred_t *);
691772Sjl139090 static int mc_close(dev_t, int, int, cred_t *);
701772Sjl139090 static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
711772Sjl139090 static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
721772Sjl139090 static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
731772Sjl139090 
742214Sav145390 static int mc_poll_init(void);
752214Sav145390 static void mc_poll_fini(void);
761772Sjl139090 static int mc_board_add(mc_opl_t *mcp);
771772Sjl139090 static int mc_board_del(mc_opl_t *mcp);
781772Sjl139090 static int mc_suspend(mc_opl_t *mcp, uint32_t flag);
791772Sjl139090 static int mc_resume(mc_opl_t *mcp, uint32_t flag);
802214Sav145390 int opl_mc_suspend(void);
812214Sav145390 int opl_mc_resume(void);
821772Sjl139090 
831772Sjl139090 static void insert_mcp(mc_opl_t *mcp);
841772Sjl139090 static void delete_mcp(mc_opl_t *mcp);
851772Sjl139090 
861772Sjl139090 static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr);
871772Sjl139090 
882662Shyw static int mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa);
891772Sjl139090 
901772Sjl139090 int mc_get_mem_unum(int, uint64_t, char *, int, int *);
912214Sav145390 int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr);
922214Sav145390 int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
932214Sav145390 int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp);
942214Sav145390 int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
952214Sav145390     int buflen, int *lenp);
962214Sav145390 mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp);
972214Sav145390 mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp);
982214Sav145390 int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank,
992214Sav145390     uint32_t mf_type, uint32_t d_slot);
1002214Sav145390 static void mc_free_dimm_list(mc_dimm_info_t *d);
1011772Sjl139090 static void mc_get_mlist(mc_opl_t *);
1022214Sav145390 static void mc_polling(void);
1032214Sav145390 static int mc_opl_get_physical_board(int);
1042214Sav145390 
1055310Sdhain static void mc_clear_rewrite(mc_opl_t *mcp, int i);
1065310Sdhain static void mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state);
107*6693Swh31274 static int mc_scf_log_event(mc_flt_page_t *flt_pag);
1085310Sdhain 
1092214Sav145390 #ifdef	DEBUG
1102214Sav145390 static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *);
1112214Sav145390 void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz);
1122214Sav145390 void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp);
1132214Sav145390 #endif
1141772Sjl139090 
1151772Sjl139090 #pragma weak opl_get_physical_board
1161772Sjl139090 extern int opl_get_physical_board(int);
1172214Sav145390 extern int plat_max_boards(void);
1181772Sjl139090 
1191772Sjl139090 /*
1201772Sjl139090  * Configuration data structures
1211772Sjl139090  */
1221772Sjl139090 static struct cb_ops mc_cb_ops = {
1231772Sjl139090 	mc_open,			/* open */
1241772Sjl139090 	mc_close,			/* close */
1251772Sjl139090 	nulldev,			/* strategy */
1261772Sjl139090 	nulldev,			/* print */
1271772Sjl139090 	nodev,				/* dump */
1281772Sjl139090 	nulldev,			/* read */
1291772Sjl139090 	nulldev,			/* write */
1301772Sjl139090 	mc_ioctl,			/* ioctl */
1311772Sjl139090 	nodev,				/* devmap */
1321772Sjl139090 	nodev,				/* mmap */
1331772Sjl139090 	nodev,				/* segmap */
1341772Sjl139090 	nochpoll,			/* poll */
1351772Sjl139090 	ddi_prop_op,			/* cb_prop_op */
1361772Sjl139090 	0,				/* streamtab */
1371772Sjl139090 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
1381772Sjl139090 	CB_REV,				/* rev */
1391772Sjl139090 	nodev,				/* cb_aread */
1401772Sjl139090 	nodev				/* cb_awrite */
1411772Sjl139090 };
1421772Sjl139090 
1431772Sjl139090 static struct dev_ops mc_ops = {
1441772Sjl139090 	DEVO_REV,			/* rev */
1451772Sjl139090 	0,				/* refcnt  */
1461772Sjl139090 	ddi_getinfo_1to1,		/* getinfo */
1471772Sjl139090 	nulldev,			/* identify */
1481772Sjl139090 	nulldev,			/* probe */
1491772Sjl139090 	mc_attach,			/* attach */
1501772Sjl139090 	mc_detach,			/* detach */
1511772Sjl139090 	nulldev,			/* reset */
1521772Sjl139090 	&mc_cb_ops,			/* cb_ops */
1531772Sjl139090 	(struct bus_ops *)0,		/* bus_ops */
1541772Sjl139090 	nulldev				/* power */
1551772Sjl139090 };
1561772Sjl139090 
1571772Sjl139090 /*
1581772Sjl139090  * Driver globals
1591772Sjl139090  */
1602214Sav145390 
1612214Sav145390 static enum {
1626297Sjl139090 	MODEL_FF1,
1636297Sjl139090 	MODEL_FF2,
1646297Sjl139090 	MODEL_DC,
1656297Sjl139090 	MODEL_IKKAKU
1662214Sav145390 } plat_model = MODEL_DC;	/* The default behaviour is DC */
1672214Sav145390 
1682214Sav145390 static struct plat_model_names {
1692214Sav145390 	const char *unit_name;
1702214Sav145390 	const char *mem_name;
1712214Sav145390 } model_names[] = {
1722214Sav145390 	{ "MBU_A", "MEMB" },
1732214Sav145390 	{ "MBU_B", "MEMB" },
1746297Sjl139090 	{ "CMU", "" },
1756297Sjl139090 	{ "MBU_A", "" }
1762214Sav145390 };
1772214Sav145390 
1782214Sav145390 /*
1792214Sav145390  * The DIMM Names for DC platform.
1802214Sav145390  * The index into this table is made up of (bank, dslot),
1812214Sav145390  * Where dslot occupies bits 0-1 and bank occupies 2-4.
1822214Sav145390  */
1832214Sav145390 static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = {
1842214Sav145390 	/* --------CMUnn----------- */
1852214Sav145390 	/* --CS0-----|--CS1------ */
1862214Sav145390 	/* -H-|--L-- | -H- | -L-- */
1872501Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
1882501Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */
1892501Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */
1902501Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */
1912501Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */
1922501Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */
1932501Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */
1942501Sraghuram 	"31A", "30A", "31B", "30B"  /* Bank 7 (MAC 3 bank 1) */
1952214Sav145390 };
1962214Sav145390 
1972214Sav145390 /*
1986297Sjl139090  * The DIMM Names for FF1/FF2/IKKAKU platforms.
1992214Sav145390  * The index into this table is made up of (board, bank, dslot),
2002214Sav145390  * Where dslot occupies bits 0-1, bank occupies 2-4 and
2012214Sav145390  * board occupies the bit 5.
2022214Sav145390  */
2032214Sav145390 static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = {
2042214Sav145390 	/* --------CMU0---------- */
2052214Sav145390 	/* --CS0-----|--CS1------ */
2062214Sav145390 	/* -H-|--L-- | -H- | -L-- */
2072501Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
2082501Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */
2092501Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */
2102501Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */
2112501Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */
2122501Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */
2132501Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */
2142501Sraghuram 	"31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */
2152214Sav145390 	/* --------CMU1---------- */
2162214Sav145390 	/* --CS0-----|--CS1------ */
2172214Sav145390 	/* -H-|--L-- | -H- | -L-- */
2182501Sraghuram 	"43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */
2192501Sraghuram 	"41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */
2202501Sraghuram 	"53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */
2212501Sraghuram 	"51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */
2222501Sraghuram 	"63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */
2232501Sraghuram 	"61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */
2242501Sraghuram 	"73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */
2252501Sraghuram 	"71A", "70A", "71B", "70B"  /* Bank 7 (MAC 3 bank 1) */
2262214Sav145390 };
2272214Sav145390 
2282214Sav145390 #define	BD_BK_SLOT_TO_INDEX(bd, bk, s)			\
2292214Sav145390 	(((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03))
2302214Sav145390 
2312214Sav145390 #define	INDEX_TO_BANK(i)			(((i) & 0x1C) >> 2)
2322214Sav145390 #define	INDEX_TO_SLOT(i)			((i) & 0x03)
2332214Sav145390 
2343045Sav145390 #define	SLOT_TO_CS(slot)	((slot & 0x3) >> 1)
2353045Sav145390 
2362214Sav145390 /* Isolation unit size is 64 MB */
2372214Sav145390 #define	MC_ISOLATION_BSIZE	(64 * 1024 * 1024)
2382214Sav145390 
2392214Sav145390 #define	MC_MAX_SPEEDS 7
2402214Sav145390 
2412214Sav145390 typedef struct {
2422214Sav145390 	uint32_t mc_speeds;
2432214Sav145390 	uint32_t mc_period;
2442214Sav145390 } mc_scan_speed_t;
2452214Sav145390 
2462214Sav145390 #define	MC_CNTL_SPEED_SHIFT 26
2472214Sav145390 
2482867Shyw /*
2492867Shyw  * In mirror mode, we normalized the bank idx to "even" since
2502867Shyw  * the HW treats them as one unit w.r.t programming.
2512867Shyw  * This bank index will be the "effective" bank index.
2522867Shyw  * All mirrored bank state info on mc_period, mc_speedup_period
2532867Shyw  * will be stored in the even bank structure to avoid code duplication.
2542867Shyw  */
2552867Shyw #define	MIRROR_IDX(bankidx)	(bankidx & ~1)
2562867Shyw 
2572214Sav145390 static mc_scan_speed_t	mc_scan_speeds[MC_MAX_SPEEDS] = {
2582214Sav145390 	{0x6 << MC_CNTL_SPEED_SHIFT, 0},
2592214Sav145390 	{0x5 << MC_CNTL_SPEED_SHIFT, 32},
2602214Sav145390 	{0x4 << MC_CNTL_SPEED_SHIFT, 64},
2612214Sav145390 	{0x3 << MC_CNTL_SPEED_SHIFT, 128},
2622214Sav145390 	{0x2 << MC_CNTL_SPEED_SHIFT, 256},
2632214Sav145390 	{0x1 << MC_CNTL_SPEED_SHIFT, 512},
2642214Sav145390 	{0x0 << MC_CNTL_SPEED_SHIFT, 1024}
2652214Sav145390 };
2662214Sav145390 
2672214Sav145390 static uint32_t	mc_max_speed = (0x6 << 26);
2682214Sav145390 
2692214Sav145390 int mc_isolation_bsize = MC_ISOLATION_BSIZE;
2702214Sav145390 int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC;
2712214Sav145390 int mc_max_scf_retry = 16;
2722214Sav145390 int mc_max_scf_logs = 64;
2732214Sav145390 int mc_max_errlog_processed = BANKNUM_PER_SB*2;
2742214Sav145390 int mc_scan_period = 12 * 60 * 60;	/* 12 hours period */
2752214Sav145390 int mc_max_rewrite_loop = 100;
2762214Sav145390 int mc_rewrite_delay = 10;
2772214Sav145390 /*
2782214Sav145390  * it takes SCF about 300 m.s. to process a requst.  We can bail out
2792214Sav145390  * if it is busy.  It does not pay to wait for it too long.
2802214Sav145390  */
2812214Sav145390 int mc_max_scf_loop = 2;
2822214Sav145390 int mc_scf_delay = 100;
2832214Sav145390 int mc_pce_dropped = 0;
2842214Sav145390 int mc_poll_priority = MINCLSYSPRI;
2855310Sdhain int mc_max_rewrite_retry = 6 * 60;
2862214Sav145390 
2872214Sav145390 
2882214Sav145390 /*
2893152Sav145390  * Mutex hierarchy in mc-opl
2902214Sav145390  * If both mcmutex and mc_lock must be held,
2912214Sav145390  * mcmutex must be acquired first, and then mc_lock.
2922214Sav145390  */
2932214Sav145390 
2941772Sjl139090 static kmutex_t mcmutex;
2952214Sav145390 mc_opl_t *mc_instances[OPL_MAX_BOARDS];
2962214Sav145390 
2972214Sav145390 static kmutex_t mc_polling_lock;
2982214Sav145390 static kcondvar_t mc_polling_cv;
2992214Sav145390 static kcondvar_t mc_poll_exit_cv;
3002214Sav145390 static int mc_poll_cmd = 0;
3012214Sav145390 static int mc_pollthr_running = 0;
3022214Sav145390 int mc_timeout_period = 0; /* this is in m.s. */
3031772Sjl139090 void *mc_statep;
3041772Sjl139090 
3051772Sjl139090 #ifdef	DEBUG
3061809Shyw int oplmc_debug = 0;
3071772Sjl139090 #endif
3081772Sjl139090 
3092214Sav145390 static int mc_debug_show_all = 0;
3101772Sjl139090 
3111772Sjl139090 extern struct mod_ops mod_driverops;
3121772Sjl139090 
3131772Sjl139090 static struct modldrv modldrv = {
3141772Sjl139090 	&mod_driverops,			/* module type, this one is a driver */
3152214Sav145390 	"OPL Memory-controller %I%",	/* module name */
3161772Sjl139090 	&mc_ops,			/* driver ops */
3171772Sjl139090 };
3181772Sjl139090 
3191772Sjl139090 static struct modlinkage modlinkage = {
3201772Sjl139090 	MODREV_1,		/* rev */
3211772Sjl139090 	(void *)&modldrv,
3221772Sjl139090 	NULL
3231772Sjl139090 };
3241772Sjl139090 
3251772Sjl139090 #pragma weak opl_get_mem_unum
3262214Sav145390 #pragma weak opl_get_mem_sid
3272214Sav145390 #pragma weak opl_get_mem_offset
3282214Sav145390 #pragma weak opl_get_mem_addr
3292214Sav145390 
3301772Sjl139090 extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *);
3312214Sav145390 extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp);
3322214Sav145390 extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp);
3332214Sav145390 extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset,
3342214Sav145390     uint64_t *paddr);
3352214Sav145390 
3361772Sjl139090 
3371772Sjl139090 /*
3381772Sjl139090  * pseudo-mc node portid format
3391772Sjl139090  *
3401772Sjl139090  *		[10]   = 0
3411772Sjl139090  *		[9]    = 1
3421772Sjl139090  *		[8]    = LSB_ID[4] = 0
3431772Sjl139090  *		[7:4]  = LSB_ID[3:0]
3441772Sjl139090  *		[3:0]  = 0
3451772Sjl139090  *
3461772Sjl139090  */
3471772Sjl139090 
3481772Sjl139090 /*
3491772Sjl139090  * These are the module initialization routines.
3501772Sjl139090  */
3511772Sjl139090 int
3521772Sjl139090 _init(void)
3531772Sjl139090 {
3542214Sav145390 	int	error;
3552214Sav145390 	int	plen;
3562214Sav145390 	char	model[20];
3572214Sav145390 	pnode_t	node;
3581772Sjl139090 
3591772Sjl139090 
3601772Sjl139090 	if ((error = ddi_soft_state_init(&mc_statep,
3611772Sjl139090 	    sizeof (mc_opl_t), 1)) != 0)
3621772Sjl139090 		return (error);
3631772Sjl139090 
3642214Sav145390 	if ((error = mc_poll_init()) != 0) {
3652214Sav145390 		ddi_soft_state_fini(&mc_statep);
3662214Sav145390 		return (error);
3672214Sav145390 	}
3682214Sav145390 
3691772Sjl139090 	mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
3701772Sjl139090 	if (&opl_get_mem_unum)
3711772Sjl139090 		opl_get_mem_unum = mc_get_mem_unum;
3722214Sav145390 	if (&opl_get_mem_sid)
3732214Sav145390 		opl_get_mem_sid = mc_get_mem_sid;
3742214Sav145390 	if (&opl_get_mem_offset)
3752214Sav145390 		opl_get_mem_offset = mc_get_mem_offset;
3762214Sav145390 	if (&opl_get_mem_addr)
3772214Sav145390 		opl_get_mem_addr = mc_get_mem_addr;
3782214Sav145390 
3792214Sav145390 	node = prom_rootnode();
3802214Sav145390 	plen = prom_getproplen(node, "model");
3812214Sav145390 
3822214Sav145390 	if (plen > 0 && plen < sizeof (model)) {
3832214Sav145390 		(void) prom_getprop(node, "model", model);
3842214Sav145390 		model[plen] = '\0';
3852214Sav145390 		if (strcmp(model, "FF1") == 0)
3862214Sav145390 			plat_model = MODEL_FF1;
3872214Sav145390 		else if (strcmp(model, "FF2") == 0)
3882214Sav145390 			plat_model = MODEL_FF2;
3892214Sav145390 		else if (strncmp(model, "DC", 2) == 0)
3902214Sav145390 			plat_model = MODEL_DC;
3916297Sjl139090 		else if (strcmp(model, "IKKAKU") == 0)
3926297Sjl139090 			plat_model = MODEL_IKKAKU;
3932214Sav145390 	}
3941772Sjl139090 
3951772Sjl139090 	error =  mod_install(&modlinkage);
3961772Sjl139090 	if (error != 0) {
3971772Sjl139090 		if (&opl_get_mem_unum)
3981772Sjl139090 			opl_get_mem_unum = NULL;
3992214Sav145390 		if (&opl_get_mem_sid)
4002214Sav145390 			opl_get_mem_sid = NULL;
4012214Sav145390 		if (&opl_get_mem_offset)
4022214Sav145390 			opl_get_mem_offset = NULL;
4032214Sav145390 		if (&opl_get_mem_addr)
4042214Sav145390 			opl_get_mem_addr = NULL;
4051772Sjl139090 		mutex_destroy(&mcmutex);
4062214Sav145390 		mc_poll_fini();
4071772Sjl139090 		ddi_soft_state_fini(&mc_statep);
4081772Sjl139090 	}
4091772Sjl139090 	return (error);
4101772Sjl139090 }
4111772Sjl139090 
4121772Sjl139090 int
4131772Sjl139090 _fini(void)
4141772Sjl139090 {
4151772Sjl139090 	int error;
4161772Sjl139090 
4171772Sjl139090 	if ((error = mod_remove(&modlinkage)) != 0)
4181772Sjl139090 		return (error);
4191772Sjl139090 
4201772Sjl139090 	if (&opl_get_mem_unum)
4211772Sjl139090 		opl_get_mem_unum = NULL;
4222214Sav145390 	if (&opl_get_mem_sid)
4232214Sav145390 		opl_get_mem_sid = NULL;
4242214Sav145390 	if (&opl_get_mem_offset)
4252214Sav145390 		opl_get_mem_offset = NULL;
4262214Sav145390 	if (&opl_get_mem_addr)
4272214Sav145390 		opl_get_mem_addr = NULL;
4282214Sav145390 
4292214Sav145390 	mutex_destroy(&mcmutex);
4302214Sav145390 	mc_poll_fini();
4311772Sjl139090 	ddi_soft_state_fini(&mc_statep);
4321772Sjl139090 
4331772Sjl139090 	return (0);
4341772Sjl139090 }
4351772Sjl139090 
4361772Sjl139090 int
4371772Sjl139090 _info(struct modinfo *modinfop)
4381772Sjl139090 {
4391772Sjl139090 	return (mod_info(&modlinkage, modinfop));
4401772Sjl139090 }
4411772Sjl139090 
4422214Sav145390 static void
4432214Sav145390 mc_polling_thread()
4442214Sav145390 {
4452214Sav145390 	mutex_enter(&mc_polling_lock);
4462214Sav145390 	mc_pollthr_running = 1;
4472214Sav145390 	while (!(mc_poll_cmd & MC_POLL_EXIT)) {
4482214Sav145390 		mc_polling();
4492214Sav145390 		cv_timedwait(&mc_polling_cv, &mc_polling_lock,
4502214Sav145390 		    ddi_get_lbolt() + mc_timeout_period);
4512214Sav145390 	}
4522214Sav145390 	mc_pollthr_running = 0;
4532214Sav145390 
4542214Sav145390 	/*
4552214Sav145390 	 * signal if any one is waiting for this thread to exit.
4562214Sav145390 	 */
4572214Sav145390 	cv_signal(&mc_poll_exit_cv);
4582214Sav145390 	mutex_exit(&mc_polling_lock);
4592214Sav145390 	thread_exit();
4602214Sav145390 	/* NOTREACHED */
4612214Sav145390 }
4622214Sav145390 
4632214Sav145390 static int
4642214Sav145390 mc_poll_init()
4652214Sav145390 {
4662214Sav145390 	mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL);
4672214Sav145390 	cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL);
4682214Sav145390 	cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL);
4692214Sav145390 	return (0);
4702214Sav145390 }
4712214Sav145390 
4722214Sav145390 static void
4732214Sav145390 mc_poll_fini()
4742214Sav145390 {
4752214Sav145390 	mutex_enter(&mc_polling_lock);
4762214Sav145390 	if (mc_pollthr_running) {
4772214Sav145390 		mc_poll_cmd = MC_POLL_EXIT;
4782214Sav145390 		cv_signal(&mc_polling_cv);
4792214Sav145390 		while (mc_pollthr_running) {
4802214Sav145390 			cv_wait(&mc_poll_exit_cv, &mc_polling_lock);
4812214Sav145390 		}
4822214Sav145390 	}
4832214Sav145390 	mutex_exit(&mc_polling_lock);
4842214Sav145390 	mutex_destroy(&mc_polling_lock);
4852214Sav145390 	cv_destroy(&mc_polling_cv);
4862214Sav145390 	cv_destroy(&mc_poll_exit_cv);
4872214Sav145390 }
4882214Sav145390 
4891772Sjl139090 static int
4901772Sjl139090 mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
4911772Sjl139090 {
4921772Sjl139090 	mc_opl_t *mcp;
4931772Sjl139090 	int instance;
4942214Sav145390 	int rv;
4951772Sjl139090 
4961772Sjl139090 	/* get the instance of this devi */
4971772Sjl139090 	instance = ddi_get_instance(devi);
4981772Sjl139090 
4991772Sjl139090 	switch (cmd) {
5001772Sjl139090 	case DDI_ATTACH:
5011772Sjl139090 		break;
5021772Sjl139090 	case DDI_RESUME:
5031772Sjl139090 		mcp = ddi_get_soft_state(mc_statep, instance);
5042214Sav145390 		rv = mc_resume(mcp, MC_DRIVER_SUSPENDED);
5052214Sav145390 		return (rv);
5061772Sjl139090 	default:
5071772Sjl139090 		return (DDI_FAILURE);
5081772Sjl139090 	}
5091772Sjl139090 
5101772Sjl139090 	if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS)
5111772Sjl139090 		return (DDI_FAILURE);
5121772Sjl139090 
513*6693Swh31274 	if (ddi_create_minor_node(devi, "mc-opl", S_IFCHR, instance,
514*6693Swh31274 	    "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
515*6693Swh31274 		MC_LOG("mc_attach: create_minor_node failed\n");
516*6693Swh31274 		return (DDI_FAILURE);
517*6693Swh31274 	}
518*6693Swh31274 
5191772Sjl139090 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
5201772Sjl139090 		goto bad;
5211772Sjl139090 	}
5221772Sjl139090 
5232214Sav145390 	if (mc_timeout_period == 0) {
5242214Sav145390 		mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi,
5255080Swh31274 		    DDI_PROP_DONTPASS, "mc-timeout-interval-sec",
5265080Swh31274 		    mc_patrol_interval_sec);
5275080Swh31274 		mc_timeout_period = drv_usectohz(1000000 *
5285080Swh31274 		    mc_patrol_interval_sec / OPL_MAX_BOARDS);
5292214Sav145390 	}
5302214Sav145390 
5311772Sjl139090 	/* set informations in mc state */
5321772Sjl139090 	mcp->mc_dip = devi;
5331772Sjl139090 
5341772Sjl139090 	if (mc_board_add(mcp))
5351772Sjl139090 		goto bad;
5361772Sjl139090 
5371772Sjl139090 	insert_mcp(mcp);
5382214Sav145390 
5392214Sav145390 	/*
5402214Sav145390 	 * Start the polling thread if it is not running already.
5412214Sav145390 	 */
5422214Sav145390 	mutex_enter(&mc_polling_lock);
5432214Sav145390 	if (!mc_pollthr_running) {
5442214Sav145390 		(void) thread_create(NULL, 0, (void (*)())mc_polling_thread,
5455080Swh31274 		    NULL, 0, &p0, TS_RUN, mc_poll_priority);
5462214Sav145390 	}
5472214Sav145390 	mutex_exit(&mc_polling_lock);
5481772Sjl139090 	ddi_report_dev(devi);
5491772Sjl139090 
5501772Sjl139090 	return (DDI_SUCCESS);
5511772Sjl139090 
5521772Sjl139090 bad:
553*6693Swh31274 	ddi_remove_minor_node(devi, NULL);
5541772Sjl139090 	ddi_soft_state_free(mc_statep, instance);
5551772Sjl139090 	return (DDI_FAILURE);
5561772Sjl139090 }
5571772Sjl139090 
5581772Sjl139090 /* ARGSUSED */
5591772Sjl139090 static int
5601772Sjl139090 mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
5611772Sjl139090 {
5622214Sav145390 	int rv;
5631772Sjl139090 	int instance;
5641772Sjl139090 	mc_opl_t *mcp;
5651772Sjl139090 
5661772Sjl139090 	/* get the instance of this devi */
5671772Sjl139090 	instance = ddi_get_instance(devi);
5681772Sjl139090 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
5691772Sjl139090 		return (DDI_FAILURE);
5701772Sjl139090 	}
5711772Sjl139090 
5721772Sjl139090 	switch (cmd) {
5731772Sjl139090 	case DDI_SUSPEND:
5742214Sav145390 		rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED);
5752214Sav145390 		return (rv);
5761772Sjl139090 	case DDI_DETACH:
5771772Sjl139090 		break;
5781772Sjl139090 	default:
5791772Sjl139090 		return (DDI_FAILURE);
5801772Sjl139090 	}
5811772Sjl139090 
5822214Sav145390 	delete_mcp(mcp);
5831772Sjl139090 	if (mc_board_del(mcp) != DDI_SUCCESS) {
5841772Sjl139090 		return (DDI_FAILURE);
5851772Sjl139090 	}
5861772Sjl139090 
587*6693Swh31274 	ddi_remove_minor_node(devi, NULL);
588*6693Swh31274 
5891772Sjl139090 	/* free up the soft state */
5901772Sjl139090 	ddi_soft_state_free(mc_statep, instance);
5911772Sjl139090 
5921772Sjl139090 	return (DDI_SUCCESS);
5931772Sjl139090 }
5941772Sjl139090 
5951772Sjl139090 /* ARGSUSED */
5961772Sjl139090 static int
5971772Sjl139090 mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
5981772Sjl139090 {
5991772Sjl139090 	return (0);
6001772Sjl139090 }
6011772Sjl139090 
6021772Sjl139090 /* ARGSUSED */
6031772Sjl139090 static int
6041772Sjl139090 mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
6051772Sjl139090 {
6061772Sjl139090 	return (0);
6071772Sjl139090 }
6081772Sjl139090 
6091772Sjl139090 /* ARGSUSED */
6101772Sjl139090 static int
6111772Sjl139090 mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
6121772Sjl139090 	int *rvalp)
6131772Sjl139090 {
614*6693Swh31274 	mc_flt_page_t flt_page;
615*6693Swh31274 
616*6693Swh31274 	if (cmd == MCIOC_FAULT_PAGE) {
617*6693Swh31274 		if (arg == NULL)
618*6693Swh31274 			return (EINVAL);
619*6693Swh31274 
620*6693Swh31274 		if (ddi_copyin((const void *)arg, (void *)&flt_page,
621*6693Swh31274 		    sizeof (mc_flt_page_t), 0) < 0)
622*6693Swh31274 			return (EFAULT);
623*6693Swh31274 
624*6693Swh31274 		return (mc_scf_log_event(&flt_page));
625*6693Swh31274 	}
6262214Sav145390 #ifdef DEBUG
6272214Sav145390 	return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp));
6282214Sav145390 #else
629*6693Swh31274 	return (ENOTTY);
6302214Sav145390 #endif
6311772Sjl139090 }
6321772Sjl139090 
6331772Sjl139090 /*
6341772Sjl139090  * PA validity check:
6352662Shyw  * This function return 1 if the PA is a valid PA
6362662Shyw  * in the running Solaris instance i.e. in physinstall
6372662Shyw  * Otherwise, return 0.
6381772Sjl139090  */
6391772Sjl139090 
6401772Sjl139090 /* ARGSUSED */
6411772Sjl139090 static int
6421772Sjl139090 pa_is_valid(mc_opl_t *mcp, uint64_t addr)
6431772Sjl139090 {
6441772Sjl139090 	if (mcp->mlist == NULL)
6451772Sjl139090 		mc_get_mlist(mcp);
6461772Sjl139090 
6471772Sjl139090 	if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) {
6481772Sjl139090 		return (1);
6491772Sjl139090 	}
6501772Sjl139090 	return (0);
6511772Sjl139090 }
6521772Sjl139090 
6531772Sjl139090 /*
6541772Sjl139090  * mac-pa translation routines.
6551772Sjl139090  *
6561772Sjl139090  *    Input: mc driver state, (LSB#, Bank#, DIMM address)
6571772Sjl139090  *    Output: physical address
6581772Sjl139090  *
6591772Sjl139090  *    Valid   - return value:  0
6601772Sjl139090  *    Invalid - return value: -1
6611772Sjl139090  */
6621772Sjl139090 static int
6631772Sjl139090 mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa)
6641772Sjl139090 {
6651772Sjl139090 	int i;
6661772Sjl139090 	uint64_t pa_offset = 0;
6671772Sjl139090 	int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1;
6681772Sjl139090 	int bank = maddr->ma_bank;
6691772Sjl139090 	mc_addr_t maddr1;
6701772Sjl139090 	int bank0, bank1;
6711772Sjl139090 
6721772Sjl139090 	MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
6735080Swh31274 	    maddr->ma_dimm_addr);
6741772Sjl139090 
6751772Sjl139090 	/* loc validity check */
6761772Sjl139090 	ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd);
6771772Sjl139090 	ASSERT(bank >= 0 && OPL_BANK_MAX > bank);
6781772Sjl139090 
6791772Sjl139090 	/* Do translation */
6801772Sjl139090 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
6811772Sjl139090 		int pa_bit = 0;
6821772Sjl139090 		int mc_bit = mcp->mc_trans_table[cs][i];
6831772Sjl139090 		if (mc_bit < MC_ADDRESS_BITS) {
6841772Sjl139090 			pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1;
6851772Sjl139090 		} else if (mc_bit == MP_NONE) {
6861772Sjl139090 			pa_bit = 0;
6871772Sjl139090 		} else if (mc_bit == MP_BANK_0) {
6881772Sjl139090 			pa_bit = bank & 1;
6891772Sjl139090 		} else if (mc_bit == MP_BANK_1) {
6901772Sjl139090 			pa_bit = (bank >> 1) & 1;
6911772Sjl139090 		} else if (mc_bit == MP_BANK_2) {
6921772Sjl139090 			pa_bit = (bank >> 2) & 1;
6931772Sjl139090 		}
6941772Sjl139090 		pa_offset |= ((uint64_t)pa_bit) << i;
6951772Sjl139090 	}
6961772Sjl139090 	*pa = mcp->mc_start_address + pa_offset;
6971772Sjl139090 	MC_LOG("pa = %lx\n", *pa);
6981772Sjl139090 
6991772Sjl139090 	if (pa_to_maddr(mcp, *pa, &maddr1) == -1) {
7002214Sav145390 		cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to "
7012214Sav145390 		    "convert PA %lx\n", maddr->ma_bd, bank,
7022214Sav145390 		    maddr->ma_dimm_addr, *pa);
7031772Sjl139090 		return (-1);
7041772Sjl139090 	}
7051772Sjl139090 
7062214Sav145390 	/*
7072214Sav145390 	 * In mirror mode, PA is always translated to the even bank.
7082214Sav145390 	 */
7091772Sjl139090 	if (IS_MIRROR(mcp, maddr->ma_bank)) {
7101772Sjl139090 		bank0 = maddr->ma_bank & ~(1);
7111772Sjl139090 		bank1 = maddr1.ma_bank & ~(1);
7121772Sjl139090 	} else {
7131772Sjl139090 		bank0 = maddr->ma_bank;
7141772Sjl139090 		bank1 = maddr1.ma_bank;
7151772Sjl139090 	}
7161772Sjl139090 	/*
7171772Sjl139090 	 * there is no need to check ma_bd because it is generated from
7181772Sjl139090 	 * mcp.  They are the same.
7191772Sjl139090 	 */
7205080Swh31274 	if ((bank0 == bank1) && (maddr->ma_dimm_addr ==
7215080Swh31274 	    maddr1.ma_dimm_addr)) {
7221772Sjl139090 		return (0);
7231772Sjl139090 	} else {
724*6693Swh31274 		MC_LOG("Translation error source /LSB%d/B%d/%x, "
7255080Swh31274 		    "PA %lx, target /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
7265080Swh31274 		    maddr->ma_dimm_addr, *pa, maddr1.ma_bd, maddr1.ma_bank,
7275080Swh31274 		    maddr1.ma_dimm_addr);
7281772Sjl139090 		return (-1);
7291772Sjl139090 	}
7301772Sjl139090 }
7311772Sjl139090 
7321772Sjl139090 /*
7331772Sjl139090  * PA to CS (used by pa_to_maddr).
7341772Sjl139090  */
7351772Sjl139090 static int
7361772Sjl139090 pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset)
7371772Sjl139090 {
7381772Sjl139090 	int i;
7392662Shyw 	int cs = 1;
7401772Sjl139090 
7411772Sjl139090 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
7421772Sjl139090 		/* MAC address bit<29> is arranged on the same PA bit */
7431772Sjl139090 		/* on both table. So we may use any table. */
7441772Sjl139090 		if (mcp->mc_trans_table[0][i] == CS_SHIFT) {
7451772Sjl139090 			cs = (pa_offset >> i) & 1;
7461772Sjl139090 			break;
7471772Sjl139090 		}
7481772Sjl139090 	}
7491772Sjl139090 	return (cs);
7501772Sjl139090 }
7511772Sjl139090 
7521772Sjl139090 /*
7531772Sjl139090  * PA to DIMM (used by pa_to_maddr).
7541772Sjl139090  */
7551772Sjl139090 /* ARGSUSED */
7561772Sjl139090 static uint32_t
7571772Sjl139090 pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset)
7581772Sjl139090 {
7591772Sjl139090 	int i;
7601772Sjl139090 	int cs = pa_to_cs(mcp, pa_offset);
7611772Sjl139090 	uint32_t dimm_addr = 0;
7621772Sjl139090 
7631772Sjl139090 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
7641772Sjl139090 		int pa_bit_value = (pa_offset >> i) & 1;
7651772Sjl139090 		int mc_bit = mcp->mc_trans_table[cs][i];
7661772Sjl139090 		if (mc_bit < MC_ADDRESS_BITS) {
7671772Sjl139090 			dimm_addr |= pa_bit_value << mc_bit;
7681772Sjl139090 		}
7691772Sjl139090 	}
7702662Shyw 	dimm_addr |= cs << CS_SHIFT;
7711772Sjl139090 	return (dimm_addr);
7721772Sjl139090 }
7731772Sjl139090 
7741772Sjl139090 /*
7751772Sjl139090  * PA to Bank (used by pa_to_maddr).
7761772Sjl139090  */
7771772Sjl139090 static int
7781772Sjl139090 pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset)
7791772Sjl139090 {
7801772Sjl139090 	int i;
7811772Sjl139090 	int cs = pa_to_cs(mcp, pa_offset);
7821772Sjl139090 	int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT];
7831772Sjl139090 
7841772Sjl139090 
7851772Sjl139090 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
7861772Sjl139090 		int pa_bit_value = (pa_offset >> i) & 1;
7871772Sjl139090 		int mc_bit = mcp->mc_trans_table[cs][i];
7881772Sjl139090 		switch (mc_bit) {
7891772Sjl139090 		case MP_BANK_0:
7901772Sjl139090 			bankno |= pa_bit_value;
7911772Sjl139090 			break;
7921772Sjl139090 		case MP_BANK_1:
7931772Sjl139090 			bankno |= pa_bit_value << 1;
7941772Sjl139090 			break;
7951772Sjl139090 		case MP_BANK_2:
7961772Sjl139090 			bankno |= pa_bit_value << 2;
7971772Sjl139090 			break;
7981772Sjl139090 		}
7991772Sjl139090 	}
8001772Sjl139090 
8011772Sjl139090 	return (bankno);
8021772Sjl139090 }
8031772Sjl139090 
8041772Sjl139090 /*
8051772Sjl139090  * PA to MAC address translation
8061772Sjl139090  *
8071772Sjl139090  *   Input: MAC driver state, physicall adress
8081772Sjl139090  *   Output: LSB#, Bank id, mac address
8091772Sjl139090  *
8101772Sjl139090  *    Valid   - return value:  0
8111772Sjl139090  *    Invalid - return value: -1
8121772Sjl139090  */
8131772Sjl139090 
8141772Sjl139090 int
8151772Sjl139090 pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr)
8161772Sjl139090 {
8171772Sjl139090 	uint64_t pa_offset;
8181772Sjl139090 
8192662Shyw 	if (!mc_rangecheck_pa(mcp, pa))
8201772Sjl139090 		return (-1);
8211772Sjl139090 
8221772Sjl139090 	/* Do translation */
8231772Sjl139090 	pa_offset = pa - mcp->mc_start_address;
8241772Sjl139090 
8251772Sjl139090 	maddr->ma_bd = mcp->mc_board_num;
8263045Sav145390 	maddr->ma_phys_bd = mcp->mc_phys_board_num;
8271772Sjl139090 	maddr->ma_bank = pa_to_bank(mcp, pa_offset);
8281772Sjl139090 	maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset);
8295080Swh31274 	MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", pa_offset, maddr->ma_bd,
8305080Swh31274 	    maddr->ma_bank, maddr->ma_dimm_addr);
8311772Sjl139090 	return (0);
8321772Sjl139090 }
8331772Sjl139090 
8342214Sav145390 /*
8352214Sav145390  * UNUM format for DC is "/CMUnn/MEMxyZ", where
8362214Sav145390  *	nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3.
8372214Sav145390  *	x = MAC 0..3
8382214Sav145390  *	y = 0..3 (slot info).
8392214Sav145390  *	Z = 'A' or 'B'
8402214Sav145390  *
8412214Sav145390  * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where
8422214Sav145390  *	x = 0..3 (MEMB number)
8432214Sav145390  *	y = 0..3 (slot info).
8442214Sav145390  *	Z = 'A' or 'B'
8452214Sav145390  *
8466297Sjl139090  * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ", where
8472214Sav145390  *	x = 0..7 (MEMB number)
8482214Sav145390  *	y = 0..3 (slot info).
8492214Sav145390  *	Z = 'A' or 'B'
8506297Sjl139090  *
8516297Sjl139090  * UNUM format for IKKAKU is "/MBU_A/MEMyZ", where
8526297Sjl139090  *	y = 0..3 (slot info).
8536297Sjl139090  *	Z = 'A' or 'B'
8546297Sjl139090  *
8552214Sav145390  */
8562214Sav145390 int
8573045Sav145390 mc_set_mem_unum(char *buf, int buflen, int sb, int bank,
8582214Sav145390     uint32_t mf_type, uint32_t d_slot)
8592214Sav145390 {
8602214Sav145390 	char *dimmnm;
8612214Sav145390 	char memb_num;
8623045Sav145390 	int cs;
8632214Sav145390 	int i;
8643045Sav145390 	int j;
8653045Sav145390 
8663045Sav145390 	cs = SLOT_TO_CS(d_slot);
8672214Sav145390 
8686297Sjl139090 	switch (plat_model) {
8696297Sjl139090 	case MODEL_DC:
8705275Stsien 		if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
8715275Stsien 		    mf_type == FLT_TYPE_PERMANENT_CE) {
8722214Sav145390 			i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
8732214Sav145390 			dimmnm = mc_dc_dimm_unum_table[i];
8742214Sav145390 			snprintf(buf, buflen, "/%s%02d/MEM%s",
8752214Sav145390 			    model_names[plat_model].unit_name, sb, dimmnm);
8762214Sav145390 		} else {
8772214Sav145390 			i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
8783045Sav145390 			j = (cs == 0) ?  i : i + 2;
8793045Sav145390 			snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
8802214Sav145390 			    model_names[plat_model].unit_name, sb,
8813045Sav145390 			    mc_dc_dimm_unum_table[j],
8823045Sav145390 			    mc_dc_dimm_unum_table[j + 1]);
8832214Sav145390 		}
8846297Sjl139090 		break;
8856297Sjl139090 	case MODEL_FF1:
8866297Sjl139090 	case MODEL_FF2:
8875275Stsien 		if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
8885275Stsien 		    mf_type == FLT_TYPE_PERMANENT_CE) {
8893045Sav145390 			i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
8902214Sav145390 			dimmnm = mc_ff_dimm_unum_table[i];
8912214Sav145390 			memb_num = dimmnm[0];
8922214Sav145390 			snprintf(buf, buflen, "/%s/%s%c/MEM%s",
8932214Sav145390 			    model_names[plat_model].unit_name,
8942214Sav145390 			    model_names[plat_model].mem_name,
8952214Sav145390 			    memb_num, &dimmnm[1]);
8962214Sav145390 		} else {
8972214Sav145390 			i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
8983045Sav145390 			j = (cs == 0) ?  i : i + 2;
8992214Sav145390 			memb_num = mc_ff_dimm_unum_table[i][0],
9005080Swh31274 			    snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s",
9012214Sav145390 			    model_names[plat_model].unit_name,
9022214Sav145390 			    model_names[plat_model].mem_name, memb_num,
9033045Sav145390 			    &mc_ff_dimm_unum_table[j][1],
9043045Sav145390 			    &mc_ff_dimm_unum_table[j + 1][1]);
9052214Sav145390 		}
9066297Sjl139090 		break;
9076297Sjl139090 	case MODEL_IKKAKU:
9086297Sjl139090 		if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
9096297Sjl139090 		    mf_type == FLT_TYPE_PERMANENT_CE) {
9106297Sjl139090 			i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
9116297Sjl139090 			dimmnm = mc_ff_dimm_unum_table[i];
9126297Sjl139090 			snprintf(buf, buflen, "/%s/MEM%s",
9136297Sjl139090 			    model_names[plat_model].unit_name, &dimmnm[1]);
9146297Sjl139090 		} else {
9156297Sjl139090 			i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
9166297Sjl139090 			j = (cs == 0) ?  i : i + 2;
9176297Sjl139090 			memb_num = mc_ff_dimm_unum_table[i][0],
9186297Sjl139090 			    snprintf(buf, buflen, "/%s/MEM%s MEM%s",
9196297Sjl139090 			    model_names[plat_model].unit_name,
9206297Sjl139090 			    &mc_ff_dimm_unum_table[j][1],
9216297Sjl139090 			    &mc_ff_dimm_unum_table[j + 1][1]);
9226297Sjl139090 		}
9236297Sjl139090 		break;
9246297Sjl139090 	default:
9256297Sjl139090 		return (-1);
9262214Sav145390 	}
9272214Sav145390 	return (0);
9282214Sav145390 }
9292214Sav145390 
9301772Sjl139090 static void
9311772Sjl139090 mc_ereport_post(mc_aflt_t *mc_aflt)
9321772Sjl139090 {
9331772Sjl139090 	char buf[FM_MAX_CLASS];
9341772Sjl139090 	char device_path[MAXPATHLEN];
9352214Sav145390 	char sid[MAXPATHLEN];
9361772Sjl139090 	nv_alloc_t *nva = NULL;
9371772Sjl139090 	nvlist_t *ereport, *detector, *resource;
9381772Sjl139090 	errorq_elem_t *eqep;
9391772Sjl139090 	int nflts;
9401772Sjl139090 	mc_flt_stat_t *flt_stat;
9412214Sav145390 	int i, n;
9422214Sav145390 	int blen = MAXPATHLEN;
9432214Sav145390 	char *p, *s = NULL;
9441772Sjl139090 	uint32_t values[2], synd[2], dslot[2];
9452214Sav145390 	uint64_t offset = (uint64_t)-1;
9462214Sav145390 	int ret = -1;
9471772Sjl139090 
9481772Sjl139090 	if (panicstr) {
9491772Sjl139090 		eqep = errorq_reserve(ereport_errorq);
9501772Sjl139090 		if (eqep == NULL)
9511772Sjl139090 			return;
9521772Sjl139090 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
9531772Sjl139090 		nva = errorq_elem_nva(ereport_errorq, eqep);
9541772Sjl139090 	} else {
9551772Sjl139090 		ereport = fm_nvlist_create(nva);
9561772Sjl139090 	}
9571772Sjl139090 
9581772Sjl139090 	/*
9591772Sjl139090 	 * Create the scheme "dev" FMRI.
9601772Sjl139090 	 */
9611772Sjl139090 	detector = fm_nvlist_create(nva);
9621772Sjl139090 	resource = fm_nvlist_create(nva);
9631772Sjl139090 
9641772Sjl139090 	nflts = mc_aflt->mflt_nflts;
9651772Sjl139090 
9661772Sjl139090 	ASSERT(nflts >= 1 && nflts <= 2);
9671772Sjl139090 
9681772Sjl139090 	flt_stat = mc_aflt->mflt_stat[0];
9691772Sjl139090 	(void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path);
9701772Sjl139090 	(void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
9711772Sjl139090 	    device_path, NULL);
9721772Sjl139090 
9731772Sjl139090 	/*
9741772Sjl139090 	 * Encode all the common data into the ereport.
9751772Sjl139090 	 */
9765080Swh31274 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", MC_OPL_ERROR_CLASS,
9775080Swh31274 	    mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : MC_OPL_MI_SUBCLASS,
9785080Swh31274 	    mc_aflt->mflt_erpt_class);
9791772Sjl139090 
9801772Sjl139090 	MC_LOG("mc_ereport_post: ereport %s\n", buf);
9811772Sjl139090 
9821772Sjl139090 
9831772Sjl139090 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
9845080Swh31274 	    fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), detector, NULL);
9851772Sjl139090 
9861772Sjl139090 	/*
9871772Sjl139090 	 * Set payload.
9881772Sjl139090 	 */
9891772Sjl139090 	fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32,
9905080Swh31274 	    flt_stat->mf_flt_maddr.ma_bd, NULL);
9911772Sjl139090 
9921772Sjl139090 	fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64,
9935080Swh31274 	    flt_stat->mf_flt_paddr, NULL);
9941772Sjl139090 
9955275Stsien 	if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE ||
9965275Stsien 	    flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
9975080Swh31274 		fm_payload_set(ereport, MC_OPL_FLT_TYPE, DATA_TYPE_UINT8,
9985080Swh31274 		    ECC_STICKY, NULL);
9991772Sjl139090 	}
10001772Sjl139090 
10011772Sjl139090 	for (i = 0; i < nflts; i++)
10021772Sjl139090 		values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank;
10031772Sjl139090 
10045080Swh31274 	fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, nflts,
10055080Swh31274 	    values, NULL);
10061772Sjl139090 
10071772Sjl139090 	for (i = 0; i < nflts; i++)
10081772Sjl139090 		values[i] = mc_aflt->mflt_stat[i]->mf_cntl;
10091772Sjl139090 
10105080Swh31274 	fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, nflts,
10115080Swh31274 	    values, NULL);
10121772Sjl139090 
10131772Sjl139090 	for (i = 0; i < nflts; i++)
10141772Sjl139090 		values[i] = mc_aflt->mflt_stat[i]->mf_err_add;
10151772Sjl139090 
10165275Stsien 	/* offset is set only for PCE and ICE */
10175275Stsien 	if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_INTERMITTENT_CE ||
10185275Stsien 	    mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) {
10192214Sav145390 		offset = values[0];
10202214Sav145390 
10212214Sav145390 	}
10225080Swh31274 	fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, nflts,
10235080Swh31274 	    values, NULL);
10241772Sjl139090 
10251772Sjl139090 	for (i = 0; i < nflts; i++)
10261772Sjl139090 		values[i] = mc_aflt->mflt_stat[i]->mf_err_log;
10271772Sjl139090 
10285080Swh31274 	fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, nflts,
10295080Swh31274 	    values, NULL);
10301772Sjl139090 
10311772Sjl139090 	for (i = 0; i < nflts; i++) {
10321772Sjl139090 		flt_stat = mc_aflt->mflt_stat[i];
10331772Sjl139090 		if (flt_stat->mf_errlog_valid) {
10341772Sjl139090 			synd[i] = flt_stat->mf_synd;
10351772Sjl139090 			dslot[i] = flt_stat->mf_dimm_slot;
10361772Sjl139090 			values[i] = flt_stat->mf_dram_place;
10371772Sjl139090 		} else {
10381772Sjl139090 			synd[i] = 0;
10391772Sjl139090 			dslot[i] = 0;
10401772Sjl139090 			values[i] = 0;
10411772Sjl139090 		}
10421772Sjl139090 	}
10431772Sjl139090 
10445080Swh31274 	fm_payload_set(ereport, MC_OPL_ERR_SYND, DATA_TYPE_UINT32_ARRAY, nflts,
10455080Swh31274 	    synd, NULL);
10465080Swh31274 
10475080Swh31274 	fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, DATA_TYPE_UINT32_ARRAY,
10485080Swh31274 	    nflts, dslot, NULL);
10495080Swh31274 
10505080Swh31274 	fm_payload_set(ereport, MC_OPL_ERR_DRAM, DATA_TYPE_UINT32_ARRAY, nflts,
10515080Swh31274 	    values, NULL);
10521772Sjl139090 
10531772Sjl139090 	device_path[0] = 0;
10541772Sjl139090 	p = &device_path[0];
10552214Sav145390 	sid[0] = 0;
10562214Sav145390 	s = &sid[0];
10572214Sav145390 	ret = 0;
10581772Sjl139090 
10591772Sjl139090 	for (i = 0; i < nflts; i++) {
10602214Sav145390 		int bank;
10611772Sjl139090 
10621772Sjl139090 		flt_stat = mc_aflt->mflt_stat[i];
10632214Sav145390 		bank = flt_stat->mf_flt_maddr.ma_bank;
10645080Swh31274 		ret = mc_set_mem_unum(p + strlen(p), blen,
10655080Swh31274 		    flt_stat->mf_flt_maddr.ma_phys_bd, bank, flt_stat->mf_type,
10665080Swh31274 		    flt_stat->mf_dimm_slot);
10672214Sav145390 
10682214Sav145390 		if (ret != 0) {
10692214Sav145390 			cmn_err(CE_WARN,
10702214Sav145390 			    "mc_ereport_post: Failed to determine the unum "
10712214Sav145390 			    "for board=%d bank=%d type=0x%x slot=0x%x",
10722214Sav145390 			    flt_stat->mf_flt_maddr.ma_bd, bank,
10732214Sav145390 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10742214Sav145390 			continue;
10751772Sjl139090 		}
10762214Sav145390 		n = strlen(device_path);
10771772Sjl139090 		blen = MAXPATHLEN - n;
10781772Sjl139090 		p = &device_path[n];
10791772Sjl139090 		if (i < (nflts - 1)) {
10801772Sjl139090 			snprintf(p, blen, " ");
10812214Sav145390 			blen--;
10822214Sav145390 			p++;
10832214Sav145390 		}
10842214Sav145390 
10852214Sav145390 		if (ret == 0) {
10862214Sav145390 			ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s),
10873045Sav145390 			    blen, flt_stat->mf_flt_maddr.ma_phys_bd, bank,
10882214Sav145390 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10892214Sav145390 
10901772Sjl139090 		}
10911772Sjl139090 	}
10921772Sjl139090 
10935080Swh31274 	(void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, NULL,
10945080Swh31274 	    device_path, (ret == 0) ? sid : NULL, (ret == 0) ? offset :
10955080Swh31274 	    (uint64_t)-1);
10965080Swh31274 
10975080Swh31274 	fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, resource,
10985080Swh31274 	    NULL);
10991772Sjl139090 
11001772Sjl139090 	if (panicstr) {
11011772Sjl139090 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
11021772Sjl139090 	} else {
11031772Sjl139090 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
11041772Sjl139090 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
11051772Sjl139090 		fm_nvlist_destroy(detector, FM_NVA_FREE);
11061772Sjl139090 		fm_nvlist_destroy(resource, FM_NVA_FREE);
11071772Sjl139090 	}
11081772Sjl139090 }
11091772Sjl139090 
11102214Sav145390 
11111772Sjl139090 static void
11121772Sjl139090 mc_err_drain(mc_aflt_t *mc_aflt)
11131772Sjl139090 {
11141772Sjl139090 	int rv;
11151772Sjl139090 	uint64_t pa = (uint64_t)(-1);
11162214Sav145390 	int i;
11171772Sjl139090 
11185080Swh31274 	MC_LOG("mc_err_drain: %s\n", mc_aflt->mflt_erpt_class);
11191772Sjl139090 	/*
11201772Sjl139090 	 * we come here only when we have:
11213152Sav145390 	 * In mirror mode: MUE, SUE
11225275Stsien 	 * In normal mode: UE, Permanent CE, Intermittent CE
11231772Sjl139090 	 */
11242214Sav145390 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
11252214Sav145390 		rv = mcaddr_to_pa(mc_aflt->mflt_mcp,
11265080Swh31274 		    &(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa);
11272662Shyw 
11282662Shyw 		/* Ensure the pa is valid (not in isolated memory block) */
11292662Shyw 		if (rv == 0 && pa_is_valid(mc_aflt->mflt_mcp, pa))
11302214Sav145390 			mc_aflt->mflt_stat[i]->mf_flt_paddr = pa;
11312214Sav145390 		else
11322214Sav145390 			mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1;
11332214Sav145390 	}
11342214Sav145390 
11352662Shyw 	MC_LOG("mc_err_drain:pa = %lx\n", pa);
11362662Shyw 
11372662Shyw 	switch (page_retire_check(pa, NULL)) {
11382662Shyw 	case 0:
11392662Shyw 	case EAGAIN:
11402662Shyw 		MC_LOG("Page retired or pending\n");
11412662Shyw 		return;
11422662Shyw 	case EIO:
11432662Shyw 		/*
11445275Stsien 		 * Do page retirement except for the PCE and ICE cases.
11452662Shyw 		 * This is taken care by the OPL DE
11462662Shyw 		 */
11475275Stsien 		if (mc_aflt->mflt_stat[0]->mf_type !=
11485275Stsien 		    FLT_TYPE_INTERMITTENT_CE &&
11495275Stsien 		    mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) {
11502662Shyw 			MC_LOG("offline page at pa %lx error %x\n", pa,
11515080Swh31274 			    mc_aflt->mflt_pr);
11522662Shyw 			(void) page_retire(pa, mc_aflt->mflt_pr);
11531772Sjl139090 		}
11542662Shyw 		break;
11552662Shyw 	case EINVAL:
11562662Shyw 	default:
11572662Shyw 		/*
11582662Shyw 		 * Some memory do not have page structure so
11592662Shyw 		 * we keep going in case of EINVAL.
11602662Shyw 		 */
11612662Shyw 		break;
11621772Sjl139090 	}
11632214Sav145390 
11642214Sav145390 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
11652214Sav145390 		mc_aflt_t mc_aflt0;
11662214Sav145390 		if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) {
11672214Sav145390 			mc_aflt0 = *mc_aflt;
11682214Sav145390 			mc_aflt0.mflt_nflts = 1;
11692214Sav145390 			mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i];
11702214Sav145390 			mc_ereport_post(&mc_aflt0);
11712214Sav145390 		}
11722214Sav145390 	}
11731772Sjl139090 }
11741772Sjl139090 
11751772Sjl139090 /*
11761772Sjl139090  * The restart address is actually defined in unit of PA[37:6]
11771772Sjl139090  * the mac patrol will convert that to dimm offset.  If the
11781772Sjl139090  * address is not in the bank, it will continue to search for
11791772Sjl139090  * the next PA that is within the bank.
11801772Sjl139090  *
11811772Sjl139090  * Also the mac patrol scans the dimms based on PA, not
11821772Sjl139090  * dimm offset.
11831772Sjl139090  */
11841772Sjl139090 static int
11852662Shyw restart_patrol(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr_info)
11861772Sjl139090 {
11871772Sjl139090 	uint64_t pa;
11881772Sjl139090 	int rv;
11892662Shyw 
11905310Sdhain 	if (MC_REWRITE_MODE(mcp, bank)) {
11915310Sdhain 		return (0);
11925310Sdhain 	}
11932662Shyw 	if (rsaddr_info == NULL || (rsaddr_info->mi_valid == 0)) {
11941772Sjl139090 		MAC_PTRL_START(mcp, bank);
11951772Sjl139090 		return (0);
11961772Sjl139090 	}
11971772Sjl139090 
11982662Shyw 	rv = mcaddr_to_pa(mcp, &rsaddr_info->mi_restartaddr, &pa);
11991772Sjl139090 	if (rv != 0) {
12001772Sjl139090 		MC_LOG("cannot convert mcaddr to pa. use auto restart\n");
12011772Sjl139090 		MAC_PTRL_START(mcp, bank);
12021772Sjl139090 		return (0);
12031772Sjl139090 	}
12041772Sjl139090 
12052662Shyw 	if (!mc_rangecheck_pa(mcp, pa)) {
12061772Sjl139090 		/* pa is not on this board, just retry */
12071772Sjl139090 		cmn_err(CE_WARN, "restart_patrol: invalid address %lx "
12085080Swh31274 		    "on board %d\n", pa, mcp->mc_board_num);
12091772Sjl139090 		MAC_PTRL_START(mcp, bank);
12101772Sjl139090 		return (0);
12111772Sjl139090 	}
12121772Sjl139090 
12131772Sjl139090 	MC_LOG("restart_patrol: pa = %lx\n", pa);
12142662Shyw 
12152662Shyw 	if (!rsaddr_info->mi_injectrestart) {
12162662Shyw 		/*
12173152Sav145390 		 * For non-error injection restart we need to
12182662Shyw 		 * determine if the current restart pa/page is
12192662Shyw 		 * a "good" page. A "good" page is a page that
12202662Shyw 		 * has not been page retired. If the current
12212662Shyw 		 * page that contains the pa is "good", we will
12222662Shyw 		 * do a HW auto restart and let HW patrol continue
12232662Shyw 		 * where it last stopped. Most desired scenario.
12242662Shyw 		 *
12252662Shyw 		 * If the current page is not "good", we will advance
12262662Shyw 		 * to the next page to find the next "good" page and
12272662Shyw 		 * restart the patrol from there.
12282662Shyw 		 */
12292662Shyw 		int wrapcount = 0;
12302662Shyw 		uint64_t origpa = pa;
12312662Shyw 		while (wrapcount < 2) {
12325080Swh31274 			if (!pa_is_valid(mcp, pa)) {
12335310Sdhain 			/*
12345310Sdhain 			 * Not in physinstall - advance to the
12355310Sdhain 			 * next memory isolation blocksize
12365310Sdhain 			 */
12375310Sdhain 			MC_LOG("Invalid PA\n");
12385310Sdhain 			pa = roundup(pa + 1, mc_isolation_bsize);
12395080Swh31274 			} else {
12405310Sdhain 			int rv;
12415310Sdhain 			if ((rv = page_retire_check(pa, NULL)) != 0 &&
12425310Sdhain 			    rv != EAGAIN) {
12435080Swh31274 					/*
12445080Swh31274 					 * The page is "good" (not retired),
12455080Swh31274 					 * we will use automatic HW restart
12465080Swh31274 					 * algorithm if this is the original
12475080Swh31274 					 * current starting page.
12485080Swh31274 					 */
12495310Sdhain 				if (pa == origpa) {
12505310Sdhain 					MC_LOG("Page has no error. "
12515310Sdhain 					    "Auto restart\n");
12525310Sdhain 					MAC_PTRL_START(mcp, bank);
12535310Sdhain 					return (0);
12545310Sdhain 				} else {
12555310Sdhain 					/*
12565310Sdhain 					 * found a subsequent good page
12575310Sdhain 					 */
12585310Sdhain 					break;
12592662Shyw 				}
12601772Sjl139090 			}
12612662Shyw 
12625310Sdhain 			/*
12635310Sdhain 			 * Skip to the next page
12645310Sdhain 			 */
12655310Sdhain 			pa = roundup(pa + 1, PAGESIZE);
12665310Sdhain 			MC_LOG("Skipping bad page to %lx\n", pa);
12675310Sdhain 			}
12685310Sdhain 
12695310Sdhain 		    /* Check to see if we hit the end of the memory range */
12705080Swh31274 			if (pa >= (mcp->mc_start_address + mcp->mc_size)) {
12715310Sdhain 			MC_LOG("Wrap around\n");
12725310Sdhain 			pa = mcp->mc_start_address;
12735310Sdhain 			wrapcount++;
12745080Swh31274 			}
12752662Shyw 		}
12762662Shyw 
12772662Shyw 		if (wrapcount > 1) {
12785080Swh31274 			MC_LOG("Failed to find a good page. Just restart\n");
12795080Swh31274 			MAC_PTRL_START(mcp, bank);
12805080Swh31274 			return (0);
12811772Sjl139090 		}
12821772Sjl139090 	}
12831772Sjl139090 
12841772Sjl139090 	/*
12852662Shyw 	 * We reached here either:
12862662Shyw 	 * 1. We are doing an error injection restart that specify
12872662Shyw 	 *    the exact pa/page to restart. OR
12882662Shyw 	 * 2. We found a subsequent good page different from the
12892662Shyw 	 *    original restart pa/page.
12902662Shyw 	 * Restart MAC patrol: PA[37:6]
12911772Sjl139090 	 */
12921772Sjl139090 	MC_LOG("restart at pa = %lx\n", pa);
12931772Sjl139090 	ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa));
12941772Sjl139090 	MAC_PTRL_START_ADD(mcp, bank);
12951772Sjl139090 
12961772Sjl139090 	return (0);
12971772Sjl139090 }
12981772Sjl139090 
12995310Sdhain static void
13005310Sdhain mc_retry_info_put(mc_retry_info_t **q, mc_retry_info_t *p)
13015310Sdhain {
13025310Sdhain 	ASSERT(p != NULL);
13035310Sdhain 	p->ri_next = *q;
13045310Sdhain 	*q = p;
13055310Sdhain }
13065310Sdhain 
13075310Sdhain static mc_retry_info_t *
13085310Sdhain mc_retry_info_get(mc_retry_info_t **q)
13095310Sdhain {
13105310Sdhain 	mc_retry_info_t *p;
13115310Sdhain 
13125310Sdhain 	if ((p = *q) != NULL) {
13135310Sdhain 		*q = p->ri_next;
13145310Sdhain 		return (p);
13155310Sdhain 	} else {
13165310Sdhain 		return (NULL);
13175310Sdhain 	}
13185310Sdhain }
13195310Sdhain 
13201772Sjl139090 /*
13211772Sjl139090  * Rewriting is used for two purposes.
13221772Sjl139090  *  - to correct the error in memory.
13231772Sjl139090  *  - to determine whether the error is permanent or intermittent.
13241772Sjl139090  * It's done by writing the address in MAC_BANKm_REWRITE_ADD
13251772Sjl139090  * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that,
13261772Sjl139090  * REW_END (and REW_CE/REW_UE if some error detected) is set when
13271772Sjl139090  * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM.
13281772Sjl139090  *
13291772Sjl139090  * Note that rewrite operation doesn't change RAW_UE to Marked UE.
13301772Sjl139090  * Therefore, we use it only CE case.
13311772Sjl139090  */
13325310Sdhain 
13331772Sjl139090 static uint32_t
13345310Sdhain do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr, int retrying)
13351772Sjl139090 {
13361772Sjl139090 	uint32_t cntl;
13371772Sjl139090 	int count = 0;
13385310Sdhain 	int max_count;
13395310Sdhain 	int retry_state;
13405310Sdhain 
13415310Sdhain 	if (retrying)
13425310Sdhain 		max_count = 1;
13435310Sdhain 	else
13445310Sdhain 		max_count = mc_max_rewrite_loop;
13455310Sdhain 
13465310Sdhain 	retry_state = RETRY_STATE_PENDING;
13475310Sdhain 
13485310Sdhain 	if (!retrying && MC_REWRITE_MODE(mcp, bank)) {
13495310Sdhain 		goto timeout;
13505310Sdhain 	}
13515310Sdhain 
13525310Sdhain 	retry_state = RETRY_STATE_ACTIVE;
13531772Sjl139090 
13541772Sjl139090 	/* first wait to make sure PTRL_STATUS is 0 */
13555310Sdhain 	while (count++ < max_count) {
13561772Sjl139090 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
13575310Sdhain 		if (!(cntl & MAC_CNTL_PTRL_STATUS)) {
13585310Sdhain 			count = 0;
13591772Sjl139090 			break;
13605310Sdhain 		}
13612214Sav145390 		drv_usecwait(mc_rewrite_delay);
13621772Sjl139090 	}
13635310Sdhain 	if (count >= max_count)
13645310Sdhain 		goto timeout;
13651772Sjl139090 
13661772Sjl139090 	count = 0;
13671772Sjl139090 
13681772Sjl139090 	ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr);
13691772Sjl139090 	MAC_REW_REQ(mcp, bank);
13701772Sjl139090 
13715310Sdhain 	retry_state = RETRY_STATE_REWRITE;
13725310Sdhain 
13731772Sjl139090 	do {
13745310Sdhain 		if (count++ > max_count) {
13755310Sdhain 			goto timeout;
13762214Sav145390 		} else {
13772214Sav145390 			drv_usecwait(mc_rewrite_delay);
13782214Sav145390 		}
13795310Sdhain 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
13801772Sjl139090 	/*
13811772Sjl139090 	 * If there are other MEMORY or PCI activities, this
13821772Sjl139090 	 * will be BUSY, else it should be set immediately
13831772Sjl139090 	 */
13841772Sjl139090 	} while (!(cntl & MAC_CNTL_REW_END));
13851772Sjl139090 
13861772Sjl139090 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
13871772Sjl139090 	return (cntl);
13885310Sdhain timeout:
13895310Sdhain 	mc_set_rewrite(mcp, bank, dimm_addr, retry_state);
13905310Sdhain 
13915310Sdhain 	return (0);
13921772Sjl139090 }
13935310Sdhain 
13945310Sdhain void
13955310Sdhain mc_clear_rewrite(mc_opl_t *mcp, int bank)
13965310Sdhain {
13975310Sdhain 	struct mc_bank *bankp;
13985310Sdhain 	mc_retry_info_t *retry;
13995310Sdhain 	uint32_t rew_addr;
14005310Sdhain 
14015310Sdhain 	bankp = &(mcp->mc_bank[bank]);
14025310Sdhain 	retry = bankp->mcb_active;
14035310Sdhain 	bankp->mcb_active = NULL;
14045310Sdhain 	mc_retry_info_put(&bankp->mcb_retry_freelist, retry);
14055310Sdhain 
14065310Sdhain again:
14075310Sdhain 	bankp->mcb_rewrite_count = 0;
14085310Sdhain 
14095310Sdhain 	while (retry = mc_retry_info_get(&bankp->mcb_retry_pending)) {
14105310Sdhain 		rew_addr = retry->ri_addr;
14115310Sdhain 		mc_retry_info_put(&bankp->mcb_retry_freelist, retry);
14125310Sdhain 		if (do_rewrite(mcp, bank, rew_addr, 1) == 0)
14135310Sdhain 			break;
14145310Sdhain 	}
14155310Sdhain 
14165310Sdhain 	/* we break out if no more pending rewrite or we got timeout again */
14175310Sdhain 
14185310Sdhain 	if (!bankp->mcb_active && !bankp->mcb_retry_pending) {
14195310Sdhain 		if (!IS_MIRROR(mcp, bank)) {
14205310Sdhain 			MC_CLEAR_REWRITE_MODE(mcp, bank);
14215310Sdhain 		} else {
14225310Sdhain 			int mbank = bank ^ 1;
14235310Sdhain 			bankp = &(mcp->mc_bank[mbank]);
14245310Sdhain 			if (!bankp->mcb_active && !bankp->mcb_retry_pending) {
14255310Sdhain 			MC_CLEAR_REWRITE_MODE(mcp, bank);
14265310Sdhain 			MC_CLEAR_REWRITE_MODE(mcp, mbank);
14275310Sdhain 			} else {
14285310Sdhain 			bank = mbank;
14295310Sdhain 			goto again;
14305310Sdhain 			}
14315310Sdhain 		}
14325310Sdhain 	}
14335310Sdhain }
14345310Sdhain 
14355310Sdhain void
14365310Sdhain mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state)
14375310Sdhain {
14385310Sdhain 	mc_retry_info_t *retry;
14395310Sdhain 	struct mc_bank *bankp;
14405310Sdhain 
14415310Sdhain 	bankp = &mcp->mc_bank[bank];
14425310Sdhain 
14435310Sdhain 	retry = mc_retry_info_get(&bankp->mcb_retry_freelist);
14445310Sdhain 
1445*6693Swh31274 	if (retry == NULL) {
1446*6693Swh31274 		mc_addr_t maddr;
1447*6693Swh31274 		uint64_t paddr;
1448*6693Swh31274 		/*
1449*6693Swh31274 		 * previous rewrite request has not completed yet.
1450*6693Swh31274 		 * So we discard this rewrite request.
1451*6693Swh31274 		 */
1452*6693Swh31274 		maddr.ma_bd = mcp->mc_board_num;
1453*6693Swh31274 		maddr.ma_bank =  bank;
1454*6693Swh31274 		maddr.ma_dimm_addr = addr;
1455*6693Swh31274 		if (mcaddr_to_pa(mcp, &maddr, &paddr) == 0) {
1456*6693Swh31274 			cmn_err(CE_WARN, "Discard CE rewrite request"
1457*6693Swh31274 			    " for 0x%lx (/LSB%d/B%d/%x).\n",
1458*6693Swh31274 			    paddr, mcp->mc_board_num, bank, addr);
1459*6693Swh31274 		} else {
1460*6693Swh31274 			cmn_err(CE_WARN, "Discard CE rewrite request"
1461*6693Swh31274 			    " for /LSB%d/B%d/%x.\n",
1462*6693Swh31274 			    mcp->mc_board_num, bank, addr);
1463*6693Swh31274 		}
1464*6693Swh31274 		return;
1465*6693Swh31274 	}
14665310Sdhain 
14675310Sdhain 	retry->ri_addr = addr;
14685310Sdhain 	retry->ri_state = state;
14695310Sdhain 
14705310Sdhain 	MC_SET_REWRITE_MODE(mcp, bank);
14715310Sdhain 
14725310Sdhain 	if ((state > RETRY_STATE_PENDING)) {
14735310Sdhain 		ASSERT(bankp->mcb_active == NULL);
14745310Sdhain 		bankp->mcb_active = retry;
14755310Sdhain 	} else {
14765310Sdhain 		mc_retry_info_put(&bankp->mcb_retry_pending, retry);
14775310Sdhain 	}
14785310Sdhain 
14795310Sdhain 	if (IS_MIRROR(mcp, bank)) {
14805310Sdhain 		int mbank = bank ^1;
14815310Sdhain 		MC_SET_REWRITE_MODE(mcp, mbank);
14825310Sdhain 	}
14835310Sdhain }
14845310Sdhain 
14851772Sjl139090 void
14861772Sjl139090 mc_process_scf_log(mc_opl_t *mcp)
14871772Sjl139090 {
14882214Sav145390 	int count;
14892214Sav145390 	int n = 0;
14901772Sjl139090 	scf_log_t *p;
14911772Sjl139090 	int bank;
14921772Sjl139090 
14932214Sav145390 	for (bank = 0; bank < BANKNUM_PER_SB; bank++) {
14945080Swh31274 		while ((p = mcp->mc_scf_log[bank]) != NULL &&
14955080Swh31274 		    (n < mc_max_errlog_processed)) {
14965310Sdhain 		ASSERT(bank == p->sl_bank);
14975310Sdhain 		count = 0;
14985310Sdhain 		while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank))
14995310Sdhain 		    & MAC_STATIC_ERR_VLD)) {
15005310Sdhain 			if (count++ >= (mc_max_scf_loop)) {
15015310Sdhain 				break;
15021772Sjl139090 			}
15035310Sdhain 			drv_usecwait(mc_scf_delay);
15045310Sdhain 		}
15055310Sdhain 
15065310Sdhain 		if (count < mc_max_scf_loop) {
15075310Sdhain 			ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank),
15085310Sdhain 			    p->sl_err_log);
15095310Sdhain 
15105310Sdhain 			ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank),
15115310Sdhain 			    p->sl_err_add|MAC_STATIC_ERR_VLD);
15125310Sdhain 			mcp->mc_scf_retry[bank] = 0;
15135310Sdhain 		} else {
15145310Sdhain 			/*
15155310Sdhain 			 * if we try too many times, just drop the req
15165310Sdhain 			 */
15175310Sdhain 			if (mcp->mc_scf_retry[bank]++ <=
15185310Sdhain 			    mc_max_scf_retry) {
15195310Sdhain 				return;
15205080Swh31274 			} else {
15215310Sdhain 				if ((++mc_pce_dropped & 0xff) == 0) {
15225310Sdhain 					cmn_err(CE_WARN, "Cannot "
1523*6693Swh31274 					    "report CE to SCF\n");
15245080Swh31274 				}
15255080Swh31274 			}
15265310Sdhain 		}
15275310Sdhain 		n++;
15285310Sdhain 		mcp->mc_scf_log[bank] = p->sl_next;
15295310Sdhain 		mcp->mc_scf_total[bank]--;
15305310Sdhain 		ASSERT(mcp->mc_scf_total[bank] >= 0);
15315310Sdhain 		kmem_free(p, sizeof (scf_log_t));
15321772Sjl139090 		}
15331772Sjl139090 	}
15341772Sjl139090 }
15351772Sjl139090 void
15361772Sjl139090 mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank)
15371772Sjl139090 {
15381772Sjl139090 	scf_log_t *p;
15391772Sjl139090 
15402214Sav145390 	if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) {
15412214Sav145390 		if ((++mc_pce_dropped & 0xff) == 0) {
1542*6693Swh31274 			cmn_err(CE_WARN, "Too many CE requests.\n");
15432214Sav145390 		}
15441772Sjl139090 		return;
15451772Sjl139090 	}
15461772Sjl139090 	p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP);
15471772Sjl139090 	p->sl_next = 0;
15481772Sjl139090 	p->sl_err_add = flt_stat->mf_err_add;
15491772Sjl139090 	p->sl_err_log = flt_stat->mf_err_log;
15501772Sjl139090 	p->sl_bank = bank;
15511772Sjl139090 
15522214Sav145390 	if (mcp->mc_scf_log[bank] == NULL) {
15531772Sjl139090 		/*
15541772Sjl139090 		 * we rely on mc_scf_log to detect NULL queue.
15551772Sjl139090 		 * mc_scf_log_tail is irrelevant is such case.
15561772Sjl139090 		 */
15572214Sav145390 		mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p;
15581772Sjl139090 	} else {
15592214Sav145390 		mcp->mc_scf_log_tail[bank]->sl_next = p;
15602214Sav145390 		mcp->mc_scf_log_tail[bank] = p;
15611772Sjl139090 	}
15622214Sav145390 	mcp->mc_scf_total[bank]++;
15631772Sjl139090 }
15641772Sjl139090 /*
15651772Sjl139090  * This routine determines what kind of CE happens, intermittent
15661772Sjl139090  * or permanent as follows. (See 4.7.3 in Columbus2 PRM.)
15671772Sjl139090  * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register.
15681772Sjl139090  * - If CE is still detected on the same address even after doing
15691772Sjl139090  *   rewrite operation twice, it is determined as permanent error.
15701772Sjl139090  * - If error is not detected anymore, it is determined as intermittent
15711772Sjl139090  *   error.
15721772Sjl139090  * - If UE is detected due to rewrite operation, it should be treated
15731772Sjl139090  *   as UE.
15741772Sjl139090  */
15751772Sjl139090 
15761772Sjl139090 /* ARGSUSED */
15771772Sjl139090 static void
15781772Sjl139090 mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error)
15791772Sjl139090 {
15801772Sjl139090 	uint32_t cntl;
15811772Sjl139090 	int i;
15821772Sjl139090 
15831772Sjl139090 	flt_stat->mf_type = FLT_TYPE_PERMANENT_CE;
15841772Sjl139090 	/*
15851772Sjl139090 	 * rewrite request 1st time reads and correct error data
15861772Sjl139090 	 * and write to DIMM.  2nd rewrite request must be issued
15871772Sjl139090 	 * after REW_CE/UE/END is 0.  When the 2nd request is completed,
15881772Sjl139090 	 * if REW_CE = 1, then it is permanent CE.
15891772Sjl139090 	 */
15901772Sjl139090 	for (i = 0; i < 2; i++) {
15915310Sdhain 		cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add, 0);
15925310Sdhain 
15935310Sdhain 		if (cntl == 0) {
15945310Sdhain 			/* timeout case */
15955310Sdhain 			return;
15965310Sdhain 		}
15971772Sjl139090 		/*
15981772Sjl139090 		 * If the error becomes UE or CMPE
15991772Sjl139090 		 * we return to the caller immediately.
16001772Sjl139090 		 */
16011772Sjl139090 		if (cntl & MAC_CNTL_REW_UE) {
16021772Sjl139090 			if (ptrl_error)
16031772Sjl139090 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE;
16041772Sjl139090 			else
16051772Sjl139090 				flt_stat->mf_cntl |= MAC_CNTL_MI_UE;
16061772Sjl139090 			flt_stat->mf_type = FLT_TYPE_UE;
16071772Sjl139090 			return;
16081772Sjl139090 		}
16091772Sjl139090 		if (cntl & MAC_CNTL_REW_CMPE) {
16101772Sjl139090 			if (ptrl_error)
16111772Sjl139090 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE;
16121772Sjl139090 			else
16131772Sjl139090 				flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE;
16141772Sjl139090 			flt_stat->mf_type = FLT_TYPE_CMPE;
16151772Sjl139090 			return;
16161772Sjl139090 		}
16171772Sjl139090 	}
16181772Sjl139090 	if (!(cntl & MAC_CNTL_REW_CE)) {
16191772Sjl139090 		flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE;
16201772Sjl139090 	}
16211772Sjl139090 
16221772Sjl139090 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
16231772Sjl139090 		/* report PERMANENT_CE to SP via SCF */
16241772Sjl139090 		if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) {
16251772Sjl139090 			mc_queue_scf_log(mcp, flt_stat, bank);
16261772Sjl139090 		}
16271772Sjl139090 	}
16281772Sjl139090 }
16291772Sjl139090 
16301772Sjl139090 #define	IS_CMPE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\
16311772Sjl139090 				MAC_CNTL_MI_CMPE))
16321772Sjl139090 #define	IS_UE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE))
16331772Sjl139090 #define	IS_CE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE))
16341772Sjl139090 #define	IS_OK(cntl, f)	(!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \
16351772Sjl139090 			MAC_CNTL_MI_ERRS)))
16361772Sjl139090 
16371772Sjl139090 
16381772Sjl139090 static int
16391772Sjl139090 IS_CE_ONLY(uint32_t cntl, int ptrl_error)
16401772Sjl139090 {
16411772Sjl139090 	if (ptrl_error) {
16421772Sjl139090 		return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE);
16431772Sjl139090 	} else {
16441772Sjl139090 		return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE);
16451772Sjl139090 	}
16461772Sjl139090 }
16471772Sjl139090 
16481772Sjl139090 void
16491772Sjl139090 mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value)
16501772Sjl139090 {
16512867Shyw 	int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
16522867Shyw 
16532867Shyw 	if (mcp->mc_speedup_period[ebank] > 0)
16542214Sav145390 		value |= mc_max_speed;
16552214Sav145390 	else
16562214Sav145390 		value |= mcp->mc_speed;
16571772Sjl139090 	ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value);
16581772Sjl139090 }
16591772Sjl139090 
16601772Sjl139090 static void
16611772Sjl139090 mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
16621772Sjl139090 {
16631772Sjl139090 	flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
16645080Swh31274 	    MAC_CNTL_PTRL_ERRS;
16651772Sjl139090 	flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank));
16661772Sjl139090 	flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank));
16671772Sjl139090 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
16683045Sav145390 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
16691772Sjl139090 	flt_stat->mf_flt_maddr.ma_bank = bank;
16701772Sjl139090 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
16711772Sjl139090 }
16721772Sjl139090 
16731772Sjl139090 static void
16741772Sjl139090 mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
16751772Sjl139090 {
16761772Sjl139090 	uint32_t status, old_status;
16771772Sjl139090 
16785080Swh31274 	status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & MAC_CNTL_MI_ERRS;
16791772Sjl139090 	old_status = 0;
16801772Sjl139090 
16811772Sjl139090 	/* we keep reading until the status is stable */
16821772Sjl139090 	while (old_status != status) {
16831772Sjl139090 		old_status = status;
16845080Swh31274 		flt_stat->mf_err_add = LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank));
16855080Swh31274 		flt_stat->mf_err_log = LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank));
16861772Sjl139090 		status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
16875080Swh31274 		    MAC_CNTL_MI_ERRS;
16881772Sjl139090 		if (status == old_status) {
16891772Sjl139090 			break;
16901772Sjl139090 		}
16911772Sjl139090 	}
16921772Sjl139090 
16931772Sjl139090 	flt_stat->mf_cntl = status;
16941772Sjl139090 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
16953045Sav145390 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
16961772Sjl139090 	flt_stat->mf_flt_maddr.ma_bank = bank;
16971772Sjl139090 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
16981772Sjl139090 }
16991772Sjl139090 
17001772Sjl139090 
17011772Sjl139090 /*
17021772Sjl139090  * Error philosophy for mirror mode:
17031772Sjl139090  *
17041772Sjl139090  * PTRL (The error address for both banks are same, since ptrl stops if it
17051772Sjl139090  * detects error.)
17063152Sav145390  * - Compare error  log CMPE.
17071772Sjl139090  *
17081772Sjl139090  * - UE-UE           Report MUE.  No rewrite.
17091772Sjl139090  *
17101772Sjl139090  * - UE-*	     UE-(CE/OK). Rewrite to scrub UE.  Report SUE.
17111772Sjl139090  *
17121772Sjl139090  * - CE-*            CE-(CE/OK). Scrub to determine if CE is permanent.
17131772Sjl139090  *                   If CE is permanent, inform SCF.  Once for each
17141772Sjl139090  *		     Dimm.  If CE becomes UE or CMPE, go back to above.
17151772Sjl139090  *
17161772Sjl139090  *
17171772Sjl139090  * MI (The error addresses for each bank are the same or different.)
17183152Sav145390  * - Compare  error  If addresses are the same.  Just CMPE, so log CMPE.
17191772Sjl139090  *		     If addresses are different (this could happen
17203152Sav145390  *		     as a result of scrubbing.  Report each separately.
17211772Sjl139090  *		     Only report error info on each side.
17221772Sjl139090  *
17231772Sjl139090  * - UE-UE           Addresses are the same.  Report MUE.
17241772Sjl139090  *		     Addresses are different.  Report SUE on each bank.
17251772Sjl139090  *		     Rewrite to clear UE.
17261772Sjl139090  *
17271772Sjl139090  * - UE-*	     UE-(CE/OK)
17281772Sjl139090  *		     Rewrite to clear UE.  Report SUE for the bank.
17291772Sjl139090  *
17301772Sjl139090  * - CE-*            CE-(CE/OK).  Scrub to determine if CE is permanent.
17311772Sjl139090  *                   If CE becomes UE or CMPE, go back to above.
17321772Sjl139090  *
17331772Sjl139090  */
17341772Sjl139090 
17351772Sjl139090 static int
17361772Sjl139090 mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat)
17371772Sjl139090 {
17381772Sjl139090 	int ptrl_error = mc_aflt->mflt_is_ptrl;
17391772Sjl139090 	int i;
17401772Sjl139090 	int rv = 0;
17415310Sdhain 	int bank;
17425310Sdhain 	int rewrite_timeout = 0;
17431772Sjl139090 
17441772Sjl139090 	MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n",
17455080Swh31274 	    flt_stat[0].mf_cntl, flt_stat[1].mf_cntl);
17461772Sjl139090 
17471772Sjl139090 	if (ptrl_error) {
17485080Swh31274 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) &
17495080Swh31274 		    MAC_CNTL_PTRL_ERRS) == 0)
17501772Sjl139090 			return (0);
17511772Sjl139090 	} else {
17525080Swh31274 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) &
17535080Swh31274 		    MAC_CNTL_MI_ERRS) == 0)
17541772Sjl139090 			return (0);
17551772Sjl139090 	}
17561772Sjl139090 
17571772Sjl139090 	/*
17581772Sjl139090 	 * First we take care of the case of CE
17591772Sjl139090 	 * because they can become UE or CMPE
17601772Sjl139090 	 */
17611772Sjl139090 	for (i = 0; i < 2; i++) {
17621772Sjl139090 		if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) {
17635310Sdhain 			bank = flt_stat[i].mf_flt_maddr.ma_bank;
17645310Sdhain 			MC_LOG("CE detected on bank %d\n", bank);
17655310Sdhain 			mc_scrub_ce(mcp, bank, &flt_stat[i], ptrl_error);
17665310Sdhain 			if (MC_REWRITE_ACTIVE(mcp, bank)) {
17675310Sdhain 				rewrite_timeout = 1;
17685310Sdhain 			}
17691772Sjl139090 			rv = 1;
17701772Sjl139090 		}
17711772Sjl139090 	}
17721772Sjl139090 
17735310Sdhain 	if (rewrite_timeout)
17745310Sdhain 		return (0);
17755310Sdhain 
17761772Sjl139090 	/* The above scrubbing can turn CE into UE or CMPE */
17771772Sjl139090 
17781772Sjl139090 	/*
17791772Sjl139090 	 * Now we distinguish two cases: same address or not
17801772Sjl139090 	 * the same address.  It might seem more intuitive to
17811772Sjl139090 	 * distinguish PTRL v.s. MI error but it is more
17821772Sjl139090 	 * complicated that way.
17831772Sjl139090 	 */
17841772Sjl139090 
17851772Sjl139090 	if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) {
17861772Sjl139090 
17871772Sjl139090 		if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) ||
17881772Sjl139090 		    IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) {
17891772Sjl139090 			flt_stat[0].mf_type = FLT_TYPE_CMPE;
17901772Sjl139090 			flt_stat[1].mf_type = FLT_TYPE_CMPE;
17911772Sjl139090 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
17921772Sjl139090 			mc_aflt->mflt_nflts = 2;
17931772Sjl139090 			mc_aflt->mflt_stat[0] = &flt_stat[0];
17941772Sjl139090 			mc_aflt->mflt_stat[1] = &flt_stat[1];
17951772Sjl139090 			mc_aflt->mflt_pr = PR_UE;
17963152Sav145390 			/*
17973152Sav145390 			 * Compare error is result of MAC internal error, so
17983152Sav145390 			 * simply log it instead of publishing an ereport. SCF
17993152Sav145390 			 * diagnoses all the MAC internal and its i/f error.
18003152Sav145390 			 */
18013152Sav145390 			MC_LOG("cmpe error detected\n");
18021772Sjl139090 			return (1);
18031772Sjl139090 		}
18041772Sjl139090 
18051772Sjl139090 		if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) &&
18065080Swh31274 		    IS_UE(flt_stat[1].mf_cntl, ptrl_error)) {
18071772Sjl139090 			/* Both side are UE's */
18081772Sjl139090 
18091772Sjl139090 			MAC_SET_ERRLOG_INFO(&flt_stat[0]);
18101772Sjl139090 			MAC_SET_ERRLOG_INFO(&flt_stat[1]);
18111772Sjl139090 			MC_LOG("MUE detected\n");
18122214Sav145390 			flt_stat[0].mf_type = FLT_TYPE_MUE;
18132214Sav145390 			flt_stat[1].mf_type = FLT_TYPE_MUE;
18141772Sjl139090 			mc_aflt->mflt_erpt_class = MC_OPL_MUE;
18151772Sjl139090 			mc_aflt->mflt_nflts = 2;
18161772Sjl139090 			mc_aflt->mflt_stat[0] = &flt_stat[0];
18171772Sjl139090 			mc_aflt->mflt_stat[1] = &flt_stat[1];
18181772Sjl139090 			mc_aflt->mflt_pr = PR_UE;
18191772Sjl139090 			mc_err_drain(mc_aflt);
18201772Sjl139090 			return (1);
18211772Sjl139090 		}
18221772Sjl139090 
18231772Sjl139090 		/* Now the only case is UE/CE, UE/OK, or don't care */
18241772Sjl139090 		for (i = 0; i < 2; i++) {
18255310Sdhain 			if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
18262214Sav145390 
18272214Sav145390 			/* rewrite can clear the one side UE error */
18282214Sav145390 
18291772Sjl139090 			if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) {
18301772Sjl139090 				(void) do_rewrite(mcp,
18311772Sjl139090 				    flt_stat[i].mf_flt_maddr.ma_bank,
18325310Sdhain 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr, 0);
18331772Sjl139090 			}
18341772Sjl139090 			flt_stat[i].mf_type = FLT_TYPE_UE;
18351772Sjl139090 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
18361772Sjl139090 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
18371772Sjl139090 			mc_aflt->mflt_stat[0] = &flt_stat[i];
18381772Sjl139090 			mc_aflt->mflt_nflts = 1;
18391772Sjl139090 			mc_aflt->mflt_pr = PR_MCE;
18401772Sjl139090 			mc_err_drain(mc_aflt);
18411772Sjl139090 			/* Once we hit a UE/CE or UE/OK case, done */
18421772Sjl139090 			return (1);
18435310Sdhain 			}
18441772Sjl139090 		}
18451772Sjl139090 
18461772Sjl139090 	} else {
18471772Sjl139090 		/*
18481772Sjl139090 		 * addresses are different. That means errors
18491772Sjl139090 		 * on the 2 banks are not related at all.
18501772Sjl139090 		 */
18511772Sjl139090 		for (i = 0; i < 2; i++) {
18525080Swh31274 			if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) {
18535080Swh31274 				flt_stat[i].mf_type = FLT_TYPE_CMPE;
18545080Swh31274 				mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
18555080Swh31274 				mc_aflt->mflt_nflts = 1;
18565080Swh31274 				mc_aflt->mflt_stat[0] = &flt_stat[i];
18575080Swh31274 				mc_aflt->mflt_pr = PR_UE;
18585080Swh31274 				/*
18595080Swh31274 				 * Compare error is result of MAC internal
18605080Swh31274 				 * error, so simply log it instead of
18615080Swh31274 				 * publishing an ereport. SCF diagnoses all
18625080Swh31274 				 * the MAC internal and its interface error.
18635080Swh31274 				 */
18645080Swh31274 				MC_LOG("cmpe error detected\n");
18655080Swh31274 				/* no more report on this bank */
18665080Swh31274 				flt_stat[i].mf_cntl = 0;
18675080Swh31274 				rv = 1;
18685080Swh31274 			}
18691772Sjl139090 		}
18701772Sjl139090 
18712214Sav145390 		/* rewrite can clear the one side UE error */
18722214Sav145390 
18731772Sjl139090 		for (i = 0; i < 2; i++) {
18745080Swh31274 			if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
18755080Swh31274 				(void) do_rewrite(mcp,
18765080Swh31274 				    flt_stat[i].mf_flt_maddr.ma_bank,
18775310Sdhain 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr,
18785310Sdhain 				    0);
18795080Swh31274 				flt_stat[i].mf_type = FLT_TYPE_UE;
18805080Swh31274 				MAC_SET_ERRLOG_INFO(&flt_stat[i]);
18815080Swh31274 				mc_aflt->mflt_erpt_class = MC_OPL_SUE;
18825080Swh31274 				mc_aflt->mflt_stat[0] = &flt_stat[i];
18835080Swh31274 				mc_aflt->mflt_nflts = 1;
18845080Swh31274 				mc_aflt->mflt_pr = PR_MCE;
18855080Swh31274 				mc_err_drain(mc_aflt);
18865080Swh31274 				rv = 1;
18875080Swh31274 			}
18881772Sjl139090 		}
18891772Sjl139090 	}
18901772Sjl139090 	return (rv);
18911772Sjl139090 }
18921772Sjl139090 static void
18932662Shyw mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
18941772Sjl139090 {
18951772Sjl139090 	mc_aflt_t mc_aflt;
18961772Sjl139090 	mc_flt_stat_t flt_stat[2], mi_flt_stat[2];
18972214Sav145390 	int i;
18982214Sav145390 	int mi_valid;
18992214Sav145390 
19002662Shyw 	ASSERT(rsaddr);
19012662Shyw 
19021772Sjl139090 	bzero(&mc_aflt, sizeof (mc_aflt_t));
19031772Sjl139090 	bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t));
19041772Sjl139090 	bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t));
19051772Sjl139090 
19063373Sbm42561 
19071772Sjl139090 	mc_aflt.mflt_mcp = mcp;
19081772Sjl139090 	mc_aflt.mflt_id = gethrtime();
19091772Sjl139090 
19101772Sjl139090 	/* Now read all the registers into flt_stat */
19111772Sjl139090 
19122214Sav145390 	for (i = 0; i < 2; i++) {
19132214Sav145390 		MC_LOG("Reading registers of bank %d\n", bank);
19142214Sav145390 		/* patrol registers */
19152214Sav145390 		mc_read_ptrl_reg(mcp, bank, &flt_stat[i]);
19162214Sav145390 
19172662Shyw 		/*
19182662Shyw 		 * In mirror mode, it is possible that only one bank
19192662Shyw 		 * may report the error. We need to check for it to
19202662Shyw 		 * ensure we pick the right addr value for patrol restart.
19212662Shyw 		 * Note that if both banks reported errors, we pick the
19222662Shyw 		 * 2nd one. Both banks should reported the same error address.
19232662Shyw 		 */
19242662Shyw 		if (flt_stat[i].mf_cntl & MAC_CNTL_PTRL_ERRS)
19252662Shyw 			rsaddr->mi_restartaddr = flt_stat[i].mf_flt_maddr;
19262214Sav145390 
19272214Sav145390 		MC_LOG("ptrl registers cntl %x add %x log %x\n",
19285080Swh31274 		    flt_stat[i].mf_cntl, flt_stat[i].mf_err_add,
19295080Swh31274 		    flt_stat[i].mf_err_log);
19302214Sav145390 
19312214Sav145390 		/* MI registers */
19322214Sav145390 		mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]);
19332214Sav145390 
19342214Sav145390 		MC_LOG("MI registers cntl %x add %x log %x\n",
19355080Swh31274 		    mi_flt_stat[i].mf_cntl, mi_flt_stat[i].mf_err_add,
19365080Swh31274 		    mi_flt_stat[i].mf_err_log);
19372214Sav145390 
19382214Sav145390 		bank = bank^1;
19392214Sav145390 	}
19401772Sjl139090 
19411772Sjl139090 	/* clear errors once we read all the registers */
19425080Swh31274 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
19431772Sjl139090 
19442214Sav145390 	MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
19452214Sav145390 
19462214Sav145390 	/* Process MI errors first */
19472214Sav145390 
19482214Sav145390 	/* if not error mode, cntl1 is 0 */
19492214Sav145390 	if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
19505080Swh31274 	    (mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
19512214Sav145390 		mi_flt_stat[0].mf_cntl = 0;
19522214Sav145390 
19532214Sav145390 	if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
19545080Swh31274 	    (mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
19552214Sav145390 		mi_flt_stat[1].mf_cntl = 0;
19562214Sav145390 
19572214Sav145390 	mc_aflt.mflt_is_ptrl = 0;
19582214Sav145390 	mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]);
19592214Sav145390 
19602214Sav145390 	if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
19615080Swh31274 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[0].mf_cntl &
19625080Swh31274 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
1963*6693Swh31274 	    (flt_stat[0].mf_err_add ==
1964*6693Swh31274 	    ROUNDDOWN(mi_flt_stat[0].mf_err_add, MC_BOUND_BYTE)) &&
19655080Swh31274 	    (((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
19665080Swh31274 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[1].mf_cntl &
19675080Swh31274 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
1968*6693Swh31274 	    (flt_stat[1].mf_err_add ==
1969*6693Swh31274 	    ROUNDDOWN(mi_flt_stat[1].mf_err_add, MC_BOUND_BYTE))) {
19702214Sav145390 #ifdef DEBUG
19712214Sav145390 		MC_LOG("discarding PTRL error because "
19722214Sav145390 		    "it is the same as MI\n");
19732214Sav145390 #endif
19742662Shyw 		rsaddr->mi_valid = mi_valid;
19752214Sav145390 		return;
19762214Sav145390 	}
19771772Sjl139090 	/* if not error mode, cntl1 is 0 */
19781772Sjl139090 	if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
19795080Swh31274 	    (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
19801772Sjl139090 		flt_stat[0].mf_cntl = 0;
19811772Sjl139090 
19821772Sjl139090 	if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
19835080Swh31274 	    (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
19841772Sjl139090 		flt_stat[1].mf_cntl = 0;
19851772Sjl139090 
19861772Sjl139090 	mc_aflt.mflt_is_ptrl = 1;
19872662Shyw 	rsaddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]);
19881772Sjl139090 }
19891772Sjl139090 static int
19901772Sjl139090 mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt,
19911772Sjl139090 	mc_flt_stat_t *flt_stat)
19921772Sjl139090 {
19931772Sjl139090 	int ptrl_error = mc_aflt->mflt_is_ptrl;
19941772Sjl139090 	int rv = 0;
19951772Sjl139090 
19961772Sjl139090 	mc_aflt->mflt_erpt_class = NULL;
19971772Sjl139090 	if (IS_UE(flt_stat->mf_cntl, ptrl_error)) {
19983152Sav145390 		MC_LOG("UE detected\n");
19991772Sjl139090 		flt_stat->mf_type = FLT_TYPE_UE;
20001772Sjl139090 		mc_aflt->mflt_erpt_class = MC_OPL_UE;
20011772Sjl139090 		mc_aflt->mflt_pr = PR_UE;
20021772Sjl139090 		MAC_SET_ERRLOG_INFO(flt_stat);
20031772Sjl139090 		rv = 1;
20041772Sjl139090 	} else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) {
20053152Sav145390 		MC_LOG("CE detected\n");
20061772Sjl139090 		MAC_SET_ERRLOG_INFO(flt_stat);
20071772Sjl139090 
20083152Sav145390 		/* Error type can change after scrubbing */
20091772Sjl139090 		mc_scrub_ce(mcp, bank, flt_stat, ptrl_error);
20105310Sdhain 		if (MC_REWRITE_ACTIVE(mcp, bank)) {
20115310Sdhain 			return (0);
20125310Sdhain 		}
20131772Sjl139090 
20145275Stsien 		if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE) {
20155275Stsien 			mc_aflt->mflt_erpt_class = MC_OPL_ICE;
20165275Stsien 			mc_aflt->mflt_pr = PR_MCE;
20175275Stsien 		} else if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
20181772Sjl139090 			mc_aflt->mflt_erpt_class = MC_OPL_CE;
20191772Sjl139090 			mc_aflt->mflt_pr = PR_MCE;
20201772Sjl139090 		} else if (flt_stat->mf_type == FLT_TYPE_UE) {
20211772Sjl139090 			mc_aflt->mflt_erpt_class = MC_OPL_UE;
20221772Sjl139090 			mc_aflt->mflt_pr = PR_UE;
20231772Sjl139090 		}
20241772Sjl139090 		rv = 1;
20251772Sjl139090 	}
20265080Swh31274 	MC_LOG("mc_process_error: fault type %x erpt %s\n", flt_stat->mf_type,
20275080Swh31274 	    mc_aflt->mflt_erpt_class);
20281772Sjl139090 	if (mc_aflt->mflt_erpt_class) {
20291772Sjl139090 		mc_aflt->mflt_stat[0] = flt_stat;
20301772Sjl139090 		mc_aflt->mflt_nflts = 1;
20311772Sjl139090 		mc_err_drain(mc_aflt);
20321772Sjl139090 	}
20331772Sjl139090 	return (rv);
20341772Sjl139090 }
20351772Sjl139090 
20361772Sjl139090 static void
20372662Shyw mc_error_handler(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
20381772Sjl139090 {
20391772Sjl139090 	mc_aflt_t mc_aflt;
20401772Sjl139090 	mc_flt_stat_t flt_stat, mi_flt_stat;
20412214Sav145390 	int mi_valid;
20421772Sjl139090 
20431772Sjl139090 	bzero(&mc_aflt, sizeof (mc_aflt_t));
20441772Sjl139090 	bzero(&flt_stat, sizeof (mc_flt_stat_t));
20451772Sjl139090 	bzero(&mi_flt_stat, sizeof (mc_flt_stat_t));
20461772Sjl139090 
20471772Sjl139090 	mc_aflt.mflt_mcp = mcp;
20481772Sjl139090 	mc_aflt.mflt_id = gethrtime();
20491772Sjl139090 
20501772Sjl139090 	/* patrol registers */
20511772Sjl139090 	mc_read_ptrl_reg(mcp, bank, &flt_stat);
20521772Sjl139090 
20532662Shyw 	ASSERT(rsaddr);
20542662Shyw 	rsaddr->mi_restartaddr = flt_stat.mf_flt_maddr;
20551772Sjl139090 
20565080Swh31274 	MC_LOG("ptrl registers cntl %x add %x log %x\n", flt_stat.mf_cntl,
20575080Swh31274 	    flt_stat.mf_err_add, flt_stat.mf_err_log);
20581772Sjl139090 
20591772Sjl139090 	/* MI registers */
20601772Sjl139090 	mc_read_mi_reg(mcp, bank, &mi_flt_stat);
20611772Sjl139090 
20622214Sav145390 
20635080Swh31274 	MC_LOG("MI registers cntl %x add %x log %x\n", mi_flt_stat.mf_cntl,
20645080Swh31274 	    mi_flt_stat.mf_err_add, mi_flt_stat.mf_err_log);
20651772Sjl139090 
20661772Sjl139090 	/* clear errors once we read all the registers */
20671772Sjl139090 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
20681772Sjl139090 
20692214Sav145390 	mc_aflt.mflt_is_ptrl = 0;
20702214Sav145390 	if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) &&
20715080Swh31274 	    ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
20725080Swh31274 	    ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
20732214Sav145390 		mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat);
20742214Sav145390 	}
20752214Sav145390 
20762214Sav145390 	if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >>
20775080Swh31274 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat.mf_cntl &
20785080Swh31274 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
2079*6693Swh31274 	    (flt_stat.mf_err_add ==
2080*6693Swh31274 	    ROUNDDOWN(mi_flt_stat.mf_err_add, MC_BOUND_BYTE))) {
20812214Sav145390 #ifdef DEBUG
20822214Sav145390 		MC_LOG("discarding PTRL error because "
20832214Sav145390 		    "it is the same as MI\n");
20842214Sav145390 #endif
20852662Shyw 		rsaddr->mi_valid = mi_valid;
20862214Sav145390 		return;
20872214Sav145390 	}
20882214Sav145390 
20891772Sjl139090 	mc_aflt.mflt_is_ptrl = 1;
20901772Sjl139090 	if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) &&
20915080Swh31274 	    ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
20925080Swh31274 	    ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
20935080Swh31274 		rsaddr->mi_valid = mc_process_error(mcp, bank, &mc_aflt,
20945080Swh31274 		    &flt_stat);
20951772Sjl139090 	}
20961772Sjl139090 }
20971772Sjl139090 /*
20981772Sjl139090  *	memory patrol error handling algorithm:
20991772Sjl139090  *	timeout() is used to do periodic polling
21001772Sjl139090  *	This is the flow chart.
21011772Sjl139090  *	timeout ->
21021772Sjl139090  *	mc_check_errors()
21031772Sjl139090  *	    if memory bank is installed, read the status register
21041772Sjl139090  *	    if any error bit is set,
21051772Sjl139090  *	    -> mc_error_handler()
21063152Sav145390  *		-> read all error registers
21071772Sjl139090  *	        -> mc_process_error()
21081772Sjl139090  *	            determine error type
21091772Sjl139090  *	            rewrite to clear error or scrub to determine CE type
21101772Sjl139090  *	            inform SCF on permanent CE
21111772Sjl139090  *	        -> mc_err_drain
21121772Sjl139090  *	            page offline processing
21131772Sjl139090  *	            -> mc_ereport_post()
21141772Sjl139090  */
21151772Sjl139090 
21161772Sjl139090 static void
21175310Sdhain mc_process_rewrite(mc_opl_t *mcp, int bank)
21185310Sdhain {
21195310Sdhain 	uint32_t rew_addr, cntl;
21205310Sdhain 	mc_retry_info_t *retry;
21215310Sdhain 	struct mc_bank *bankp;
21225310Sdhain 
21235310Sdhain 	bankp = &(mcp->mc_bank[bank]);
21245310Sdhain 	retry = bankp->mcb_active;
21255310Sdhain 	if (retry == NULL)
21265310Sdhain 		return;
21275310Sdhain 
21285310Sdhain 	if (retry->ri_state <= RETRY_STATE_ACTIVE) {
21295310Sdhain 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
21305310Sdhain 		if (cntl & MAC_CNTL_PTRL_STATUS)
21315310Sdhain 			return;
21325310Sdhain 		rew_addr = retry->ri_addr;
21335310Sdhain 		ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), rew_addr);
21345310Sdhain 		MAC_REW_REQ(mcp, bank);
21355310Sdhain 
21365310Sdhain 		retry->ri_state = RETRY_STATE_REWRITE;
21375310Sdhain 	}
21385310Sdhain 
21395310Sdhain 	cntl = ldphysio(MAC_PTRL_CNTL(mcp, bank));
21405310Sdhain 
21415310Sdhain 	if (cntl & MAC_CNTL_REW_END) {
21425310Sdhain 		MAC_CLEAR_ERRS(mcp, bank,
21435310Sdhain 		    MAC_CNTL_REW_ERRS);
21445310Sdhain 		mc_clear_rewrite(mcp, bank);
21455310Sdhain 	} else {
21465310Sdhain 		/*
21475310Sdhain 		 * If the rewrite does not complete in
21485310Sdhain 		 * 1 hour, we have to consider this a HW
21495310Sdhain 		 * failure.  However, there is no recovery
21505310Sdhain 		 * mechanism.  The only thing we can do
21515310Sdhain 		 * to to print a warning message to the
21525310Sdhain 		 * console.  We continue to increment the
21535310Sdhain 		 * counter but we only print the message
21545310Sdhain 		 * once.  It will take the counter a long
21555310Sdhain 		 * time to wrap around and the user might
21565310Sdhain 		 * see a second message.  In practice,
21575310Sdhain 		 * we have never hit this condition but
21585310Sdhain 		 * we have to keep the code here just in case.
21595310Sdhain 		 */
21605310Sdhain 		if (++mcp->mc_bank[bank].mcb_rewrite_count
21615310Sdhain 		    == mc_max_rewrite_retry) {
21625310Sdhain 			cmn_err(CE_WARN, "Memory patrol feature is"
21635310Sdhain 			" partly suspended on /LSB%d/B%d"
21645310Sdhain 			" due to heavy memory load,"
21655310Sdhain 			" and it will restart"
21665310Sdhain 			" automatically.\n", mcp->mc_board_num,
21675310Sdhain 			    bank);
21685310Sdhain 		}
21695310Sdhain 	}
21705310Sdhain }
21715310Sdhain 
21725310Sdhain static void
21731772Sjl139090 mc_check_errors_func(mc_opl_t *mcp)
21741772Sjl139090 {
21752662Shyw 	mc_rsaddr_info_t rsaddr_info;
21761772Sjl139090 	int i, error_count = 0;
21771772Sjl139090 	uint32_t stat, cntl;
21782214Sav145390 	int running;
21792494Shyw 	int wrapped;
21802867Shyw 	int ebk;
21811772Sjl139090 
21821772Sjl139090 	/*
21831772Sjl139090 	 * scan errors.
21841772Sjl139090 	 */
21852214Sav145390 	if (mcp->mc_status & MC_MEMORYLESS)
21862214Sav145390 		return;
21872214Sav145390 
21881772Sjl139090 	for (i = 0; i < BANKNUM_PER_SB; i++) {
21891772Sjl139090 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
21905310Sdhain 			if (MC_REWRITE_ACTIVE(mcp, i)) {
21915310Sdhain 				mc_process_rewrite(mcp, i);
21925310Sdhain 			}
21931772Sjl139090 			stat = ldphysio(MAC_PTRL_STAT(mcp, i));
21941772Sjl139090 			cntl = ldphysio(MAC_PTRL_CNTL(mcp, i));
21952214Sav145390 			running = cntl & MAC_CNTL_PTRL_START;
21962494Shyw 			wrapped = cntl & MAC_CNTL_PTRL_ADD_MAX;
21972494Shyw 
21982867Shyw 			/* Compute the effective bank idx */
21992867Shyw 			ebk = (IS_MIRROR(mcp, i)) ? MIRROR_IDX(i) : i;
22002867Shyw 
22012494Shyw 			if (mc_debug_show_all || stat) {
22022494Shyw 				MC_LOG("/LSB%d/B%d stat %x cntl %x\n",
22035080Swh31274 				    mcp->mc_board_num, i, stat, cntl);
22042494Shyw 			}
22052494Shyw 
22062494Shyw 			/*
22072494Shyw 			 * Update stats and reset flag if the HW patrol
22082494Shyw 			 * wrapped around in its scan.
22092494Shyw 			 */
22102494Shyw 			if (wrapped) {
22111772Sjl139090 				MAC_CLEAR_MAX(mcp, i);
22122867Shyw 				mcp->mc_period[ebk]++;
22136297Sjl139090 				if (IS_MIRROR(mcp, i)) {
22145080Swh31274 					MC_LOG("mirror mc period %ld on "
22155080Swh31274 					    "/LSB%d/B%d\n", mcp->mc_period[ebk],
22165080Swh31274 					    mcp->mc_board_num, i);
22176297Sjl139090 				} else {
22185080Swh31274 					MC_LOG("mc period %ld on "
22195080Swh31274 					    "/LSB%d/B%d\n", mcp->mc_period[ebk],
22205080Swh31274 					    mcp->mc_board_num, i);
22212867Shyw 				}
22222494Shyw 			}
22232494Shyw 
22242494Shyw 			if (running) {
22252494Shyw 				/*
22262494Shyw 				 * Mac patrol HW is still running.
22272494Shyw 				 * Normally when an error is detected,
22282494Shyw 				 * the HW patrol will stop so that we
22292494Shyw 				 * can collect error data for reporting.
22302494Shyw 				 * Certain errors (MI errors) detected may not
22312494Shyw 				 * cause the HW patrol to stop which is a
22322494Shyw 				 * problem since we cannot read error data while
22332494Shyw 				 * the HW patrol is running. SW is not allowed
22342494Shyw 				 * to stop the HW patrol while it is running
22352494Shyw 				 * as it may cause HW inconsistency. This is
22362494Shyw 				 * described in a HW errata.
22372494Shyw 				 * In situations where we detected errors
22382494Shyw 				 * that may not cause the HW patrol to stop.
22392494Shyw 				 * We speed up the HW patrol scanning in
22402494Shyw 				 * the hope that it will find the 'real' PTRL
22412494Shyw 				 * errors associated with the previous errors
22422494Shyw 				 * causing the HW to finally stop so that we
22432494Shyw 				 * can do the reporting.
22442494Shyw 				 */
22452494Shyw 				/*
22462494Shyw 				 * Check to see if we did speed up
22472494Shyw 				 * the HW patrol due to previous errors
22482494Shyw 				 * detected that did not cause the patrol
22492494Shyw 				 * to stop. We only do it if HW patrol scan
22502494Shyw 				 * wrapped (counted as completing a 'period').
22512494Shyw 				 */
22522867Shyw 				if (mcp->mc_speedup_period[ebk] > 0) {
22535080Swh31274 					if (wrapped &&
22545080Swh31274 					    (--mcp->mc_speedup_period[ebk] ==
22555080Swh31274 					    0)) {
22565080Swh31274 						/*
22575080Swh31274 						 * We did try to speed up.
22585080Swh31274 						 * The speed up period has
22595080Swh31274 						 * expired and the HW patrol
22605080Swh31274 						 * is still running.  The
22615080Swh31274 						 * errors must be intermittent.
22625080Swh31274 						 * We have no choice but to
22635080Swh31274 						 * ignore them, reset the scan
22645080Swh31274 						 * speed to normal and clear
22655080Swh31274 						 * the MI error bits. For
22665080Swh31274 						 * mirror mode, we need to
22675080Swh31274 						 * clear errors on both banks.
22685080Swh31274 						 */
22695080Swh31274 						MC_LOG("Clearing MI errors\n");
22705080Swh31274 						MAC_CLEAR_ERRS(mcp, i,
22715080Swh31274 						    MAC_CNTL_MI_ERRS);
22725080Swh31274 
22735080Swh31274 						if (IS_MIRROR(mcp, i)) {
22745080Swh31274 							MC_LOG("Clearing "
22755080Swh31274 							    "Mirror MI errs\n");
22765080Swh31274 							MAC_CLEAR_ERRS(mcp,
22775080Swh31274 							    i^1,
22785080Swh31274 							    MAC_CNTL_MI_ERRS);
22795080Swh31274 						}
22802867Shyw 					}
22812494Shyw 				} else if (stat & MAC_STAT_MI_ERRS) {
22822494Shyw 					/*
22832494Shyw 					 * MI errors detected but we cannot
22842494Shyw 					 * report them since the HW patrol
22852494Shyw 					 * is still running.
22862494Shyw 					 * We will attempt to speed up the
22872494Shyw 					 * scanning and hopefully the HW
22882494Shyw 					 * can detect PRTL errors at the same
22892494Shyw 					 * location that cause the HW patrol
22902494Shyw 					 * to stop.
22912494Shyw 					 */
22922867Shyw 					mcp->mc_speedup_period[ebk] = 2;
22932214Sav145390 					MAC_CMD(mcp, i, 0);
22942214Sav145390 				}
22952494Shyw 			} else if (stat & (MAC_STAT_PTRL_ERRS |
22962494Shyw 			    MAC_STAT_MI_ERRS)) {
22972494Shyw 				/*
22982494Shyw 				 * HW Patrol has stopped and we found errors.
22992494Shyw 				 * Proceed to collect and report error info.
23002494Shyw 				 */
23012867Shyw 				mcp->mc_speedup_period[ebk] = 0;
23022662Shyw 				rsaddr_info.mi_valid = 0;
23032662Shyw 				rsaddr_info.mi_injectrestart = 0;
23042662Shyw 				if (IS_MIRROR(mcp, i)) {
23055080Swh31274 					mc_error_handler_mir(mcp, i,
23065080Swh31274 					    &rsaddr_info);
23072662Shyw 				} else {
23085080Swh31274 					mc_error_handler(mcp, i, &rsaddr_info);
23092662Shyw 				}
23102494Shyw 
23112494Shyw 				error_count++;
23122662Shyw 				restart_patrol(mcp, i, &rsaddr_info);
23131772Sjl139090 			} else {
23142494Shyw 				/*
23152494Shyw 				 * HW patrol scan has apparently stopped
23162494Shyw 				 * but no errors detected/flagged.
23172494Shyw 				 * Restart the HW patrol just to be sure.
23182867Shyw 				 * In mirror mode, the odd bank might have
23192867Shyw 				 * reported errors that caused the patrol to
23202867Shyw 				 * stop. We'll defer the restart to the odd
23212867Shyw 				 * bank in this case.
23222494Shyw 				 */
23232867Shyw 				if (!IS_MIRROR(mcp, i) || (i & 0x1))
23242867Shyw 					restart_patrol(mcp, i, NULL);
23251772Sjl139090 			}
23261772Sjl139090 		}
23271772Sjl139090 	}
23281772Sjl139090 	if (error_count > 0)
23291772Sjl139090 		mcp->mc_last_error += error_count;
23301772Sjl139090 	else
23311772Sjl139090 		mcp->mc_last_error = 0;
23321772Sjl139090 }
23331772Sjl139090 
23342214Sav145390 /*
23352214Sav145390  * mc_polling -- Check errors for only one instance,
23362214Sav145390  * but process errors for all instances to make sure we drain the errors
23372214Sav145390  * faster than they can be accumulated.
23382214Sav145390  *
23392214Sav145390  * Polling on each board should be done only once per each
23402214Sav145390  * mc_patrol_interval_sec.  This is equivalent to setting mc_tick_left
23412214Sav145390  * to OPL_MAX_BOARDS and decrement by 1 on each timeout.
23422214Sav145390  * Once mc_tick_left becomes negative, the board becomes a candidate
23432214Sav145390  * for polling because it has waited for at least
23442214Sav145390  * mc_patrol_interval_sec's long.    If mc_timeout_period is calculated
23453152Sav145390  * differently, this has to be updated accordingly.
23462214Sav145390  */
23471772Sjl139090 
23481772Sjl139090 static void
23492214Sav145390 mc_polling(void)
23501772Sjl139090 {
23512214Sav145390 	int i, scan_error;
23522214Sav145390 	mc_opl_t *mcp;
23532214Sav145390 
23542214Sav145390 
23552214Sav145390 	scan_error = 1;
23562214Sav145390 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
23572214Sav145390 		mutex_enter(&mcmutex);
23582214Sav145390 		if ((mcp = mc_instances[i]) == NULL) {
23592214Sav145390 			mutex_exit(&mcmutex);
23602214Sav145390 			continue;
23612214Sav145390 		}
23622214Sav145390 		mutex_enter(&mcp->mc_lock);
23632214Sav145390 		mutex_exit(&mcmutex);
23642662Shyw 		if (!(mcp->mc_status & MC_POLL_RUNNING)) {
23652662Shyw 			mutex_exit(&mcp->mc_lock);
23662662Shyw 			continue;
23672662Shyw 		}
23682214Sav145390 		if (scan_error && mcp->mc_tick_left <= 0) {
23692214Sav145390 			mc_check_errors_func((void *)mcp);
23702214Sav145390 			mcp->mc_tick_left = OPL_MAX_BOARDS;
23712214Sav145390 			scan_error = 0;
23722214Sav145390 		} else {
23732214Sav145390 			mcp->mc_tick_left--;
23742214Sav145390 		}
23752214Sav145390 		mc_process_scf_log(mcp);
23762214Sav145390 		mutex_exit(&mcp->mc_lock);
23771772Sjl139090 	}
23781772Sjl139090 }
23791772Sjl139090 
23801772Sjl139090 static void
23811772Sjl139090 get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr)
23821772Sjl139090 {
23831772Sjl139090 	maddr->ma_bd = mcp->mc_board_num;
23841772Sjl139090 	maddr->ma_bank = bank;
23851772Sjl139090 	maddr->ma_dimm_addr = 0;
23861772Sjl139090 }
23871772Sjl139090 
23881772Sjl139090 typedef struct mc_mem_range {
23891772Sjl139090 	uint64_t	addr;
23901772Sjl139090 	uint64_t	size;
23911772Sjl139090 } mc_mem_range_t;
23921772Sjl139090 
23931772Sjl139090 static int
23941772Sjl139090 get_base_address(mc_opl_t *mcp)
23951772Sjl139090 {
23961772Sjl139090 	mc_mem_range_t *mem_range;
23971772Sjl139090 	int len;
23981772Sjl139090 
23991772Sjl139090 	if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
24005080Swh31274 	    "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) {
24011772Sjl139090 		return (DDI_FAILURE);
24021772Sjl139090 	}
24031772Sjl139090 
24041772Sjl139090 	mcp->mc_start_address = mem_range->addr;
24051772Sjl139090 	mcp->mc_size = mem_range->size;
24061772Sjl139090 
24071772Sjl139090 	kmem_free(mem_range, len);
24081772Sjl139090 	return (DDI_SUCCESS);
24091772Sjl139090 }
24101772Sjl139090 
24111772Sjl139090 struct mc_addr_spec {
24121772Sjl139090 	uint32_t bank;
24131772Sjl139090 	uint32_t phys_hi;
24141772Sjl139090 	uint32_t phys_lo;
24151772Sjl139090 };
24161772Sjl139090 
24171772Sjl139090 #define	REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo)
24181772Sjl139090 
24191772Sjl139090 static char *mc_tbl_name[] = {
24201772Sjl139090 	"cs0-mc-pa-trans-table",
24211772Sjl139090 	"cs1-mc-pa-trans-table"
24221772Sjl139090 };
24231772Sjl139090 
24242662Shyw /*
24252662Shyw  * This routine performs a rangecheck for a given PA
24262662Shyw  * to see if it belongs to the memory range for this board.
24272662Shyw  * Return 1 if it is valid (within the range) and 0 otherwise
24282662Shyw  */
24291772Sjl139090 static int
24302662Shyw mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa)
24311772Sjl139090 {
24325080Swh31274 	if ((pa < mcp->mc_start_address) || (mcp->mc_start_address +
24335080Swh31274 	    mcp->mc_size <= pa))
24342662Shyw 		return (0);
24352662Shyw 	else
24362662Shyw 		return (1);
24371772Sjl139090 }
24381772Sjl139090 
24391772Sjl139090 static void
24401772Sjl139090 mc_memlist_delete(struct memlist *mlist)
24411772Sjl139090 {
24421772Sjl139090 	struct memlist *ml;
24431772Sjl139090 
24441772Sjl139090 	for (ml = mlist; ml; ml = mlist) {
24451772Sjl139090 		mlist = ml->next;
24461772Sjl139090 		kmem_free(ml, sizeof (struct memlist));
24471772Sjl139090 	}
24481772Sjl139090 }
24491772Sjl139090 
24501772Sjl139090 static struct memlist *
24511772Sjl139090 mc_memlist_dup(struct memlist *mlist)
24521772Sjl139090 {
24531772Sjl139090 	struct memlist *hl = NULL, *tl, **mlp;
24541772Sjl139090 
24551772Sjl139090 	if (mlist == NULL)
24561772Sjl139090 		return (NULL);
24571772Sjl139090 
24581772Sjl139090 	mlp = &hl;
24591772Sjl139090 	tl = *mlp;
24601772Sjl139090 	for (; mlist; mlist = mlist->next) {
24611772Sjl139090 		*mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP);
24621772Sjl139090 		(*mlp)->address = mlist->address;
24631772Sjl139090 		(*mlp)->size = mlist->size;
24641772Sjl139090 		(*mlp)->prev = tl;
24651772Sjl139090 		tl = *mlp;
24661772Sjl139090 		mlp = &((*mlp)->next);
24671772Sjl139090 	}
24681772Sjl139090 	*mlp = NULL;
24691772Sjl139090 
24701772Sjl139090 	return (hl);
24711772Sjl139090 }
24721772Sjl139090 
24731772Sjl139090 
24741772Sjl139090 static struct memlist *
24751772Sjl139090 mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len)
24761772Sjl139090 {
24771772Sjl139090 	uint64_t	end;
24781772Sjl139090 	struct memlist	*ml, *tl, *nlp;
24791772Sjl139090 
24801772Sjl139090 	if (mlist == NULL)
24811772Sjl139090 		return (NULL);
24821772Sjl139090 
24831772Sjl139090 	end = base + len;
24841772Sjl139090 	if ((end <= mlist->address) || (base == end))
24851772Sjl139090 		return (mlist);
24861772Sjl139090 
24871772Sjl139090 	for (tl = ml = mlist; ml; tl = ml, ml = nlp) {
24881772Sjl139090 		uint64_t	mend;
24891772Sjl139090 
24901772Sjl139090 		nlp = ml->next;
24911772Sjl139090 
24921772Sjl139090 		if (end <= ml->address)
24931772Sjl139090 			break;
24941772Sjl139090 
24951772Sjl139090 		mend = ml->address + ml->size;
24961772Sjl139090 		if (base < mend) {
24971772Sjl139090 			if (base <= ml->address) {
24981772Sjl139090 				ml->address = end;
24991772Sjl139090 				if (end >= mend)
25001772Sjl139090 					ml->size = 0ull;
25011772Sjl139090 				else
25021772Sjl139090 					ml->size = mend - ml->address;
25031772Sjl139090 			} else {
25041772Sjl139090 				ml->size = base - ml->address;
25051772Sjl139090 				if (end < mend) {
25061772Sjl139090 					struct memlist	*nl;
25071772Sjl139090 					/*
25081772Sjl139090 					 * splitting an memlist entry.
25091772Sjl139090 					 */
25101772Sjl139090 					nl = kmem_alloc(sizeof (struct memlist),
25115080Swh31274 					    KM_SLEEP);
25121772Sjl139090 					nl->address = end;
25131772Sjl139090 					nl->size = mend - nl->address;
25141772Sjl139090 					if ((nl->next = nlp) != NULL)
25151772Sjl139090 						nlp->prev = nl;
25161772Sjl139090 					nl->prev = ml;
25171772Sjl139090 					ml->next = nl;
25181772Sjl139090 					nlp = nl;
25191772Sjl139090 				}
25201772Sjl139090 			}
25211772Sjl139090 			if (ml->size == 0ull) {
25221772Sjl139090 				if (ml == mlist) {
25231772Sjl139090 					if ((mlist = nlp) != NULL)
25241772Sjl139090 						nlp->prev = NULL;
25251772Sjl139090 					kmem_free(ml, sizeof (struct memlist));
25261772Sjl139090 					if (mlist == NULL)
25271772Sjl139090 						break;
25281772Sjl139090 					ml = nlp;
25291772Sjl139090 				} else {
25301772Sjl139090 					if ((tl->next = nlp) != NULL)
25311772Sjl139090 						nlp->prev = tl;
25321772Sjl139090 					kmem_free(ml, sizeof (struct memlist));
25331772Sjl139090 					ml = tl;
25341772Sjl139090 				}
25351772Sjl139090 			}
25361772Sjl139090 		}
25371772Sjl139090 	}
25381772Sjl139090 
25391772Sjl139090 	return (mlist);
25401772Sjl139090 }
25411772Sjl139090 
25421772Sjl139090 static void
25431772Sjl139090 mc_get_mlist(mc_opl_t *mcp)
25441772Sjl139090 {
25451772Sjl139090 	struct memlist *mlist;
25461772Sjl139090 
25471772Sjl139090 	memlist_read_lock();
25481772Sjl139090 	mlist = mc_memlist_dup(phys_install);
25491772Sjl139090 	memlist_read_unlock();
25501772Sjl139090 
25511772Sjl139090 	if (mlist) {
25521772Sjl139090 		mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address);
25531772Sjl139090 	}
25541772Sjl139090 
25551772Sjl139090 	if (mlist) {
25561772Sjl139090 		uint64_t startpa, endpa;
25571772Sjl139090 
25581772Sjl139090 		startpa = mcp->mc_start_address + mcp->mc_size;
25591772Sjl139090 		endpa = ptob(physmax + 1);
25601772Sjl139090 		if (endpa > startpa) {
25615080Swh31274 			mlist = mc_memlist_del_span(mlist, startpa,
25625080Swh31274 			    endpa - startpa);
25631772Sjl139090 		}
25641772Sjl139090 	}
25651772Sjl139090 
25661772Sjl139090 	if (mlist) {
25671772Sjl139090 		mcp->mlist = mlist;
25681772Sjl139090 	}
25691772Sjl139090 }
25701772Sjl139090 
25711772Sjl139090 int
25721772Sjl139090 mc_board_add(mc_opl_t *mcp)
25731772Sjl139090 {
25741772Sjl139090 	struct mc_addr_spec *macaddr;
25752214Sav145390 	cs_status_t *cs_status;
25762214Sav145390 	int len, len1, i, bk, cc;
25772662Shyw 	mc_rsaddr_info_t rsaddr;
25781772Sjl139090 	uint32_t mirr;
25792214Sav145390 	int nbanks = 0;
25802214Sav145390 	uint64_t nbytes = 0;
25815080Swh31274 	int mirror_mode = 0;
25825080Swh31274 	int ret;
25831772Sjl139090 
25841772Sjl139090 	/*
25851772Sjl139090 	 * Get configurations from "pseudo-mc" node which includes:
25861772Sjl139090 	 * board# : LSB number
25871772Sjl139090 	 * mac-addr : physical base address of MAC registers
25881772Sjl139090 	 * csX-mac-pa-trans-table: translation table from DIMM address
25891772Sjl139090 	 *			to physical address or vice versa.
25901772Sjl139090 	 */
25911772Sjl139090 	mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip,
25925080Swh31274 	    DDI_PROP_DONTPASS, "board#", -1);
25931772Sjl139090 
25942214Sav145390 	if (mcp->mc_board_num == -1) {
25952214Sav145390 		return (DDI_FAILURE);
25962214Sav145390 	}
25972214Sav145390 
25981772Sjl139090 	/*
25991772Sjl139090 	 * Get start address in this CAB. It can be gotten from
26001772Sjl139090 	 * "sb-mem-ranges" property.
26011772Sjl139090 	 */
26021772Sjl139090 
26031772Sjl139090 	if (get_base_address(mcp) == DDI_FAILURE) {
26041772Sjl139090 		return (DDI_FAILURE);
26051772Sjl139090 	}
26061772Sjl139090 	/* get mac-pa trans tables */
26071772Sjl139090 	for (i = 0; i < MC_TT_CS; i++) {
26081772Sjl139090 		len = MC_TT_ENTRIES;
26091772Sjl139090 		cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip,
26105080Swh31274 		    DDI_PROP_DONTPASS, mc_tbl_name[i],
26115080Swh31274 		    (caddr_t)mcp->mc_trans_table[i], &len);
26121772Sjl139090 
26131772Sjl139090 		if (cc != DDI_SUCCESS) {
26141772Sjl139090 			bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES);
26151772Sjl139090 		}
26161772Sjl139090 	}
26171772Sjl139090 	mcp->mlist = NULL;
26181772Sjl139090 
26191772Sjl139090 	mc_get_mlist(mcp);
26201772Sjl139090 
26211772Sjl139090 	/* initialize bank informations */
26221772Sjl139090 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
26235080Swh31274 	    "mc-addr", (caddr_t)&macaddr, &len);
26241772Sjl139090 	if (cc != DDI_SUCCESS) {
26251772Sjl139090 		cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc);
26262214Sav145390 		return (DDI_FAILURE);
26272214Sav145390 	}
26282214Sav145390 
26292214Sav145390 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
26305080Swh31274 	    "cs-status", (caddr_t)&cs_status, &len1);
26312214Sav145390 
26322214Sav145390 	if (cc != DDI_SUCCESS) {
26332214Sav145390 		if (len > 0)
26342214Sav145390 			kmem_free(macaddr, len);
26352214Sav145390 		cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc);
26361772Sjl139090 		return (DDI_FAILURE);
26371772Sjl139090 	}
26383045Sav145390 	/* get the physical board number for a given logical board number */
26393045Sav145390 	mcp->mc_phys_board_num = mc_opl_get_physical_board(mcp->mc_board_num);
26403045Sav145390 
26413045Sav145390 	if (mcp->mc_phys_board_num < 0) {
26423045Sav145390 		if (len > 0)
26433045Sav145390 			kmem_free(macaddr, len);
26443045Sav145390 		cmn_err(CE_WARN, "Unable to obtain the physical board number");
26453045Sav145390 		return (DDI_FAILURE);
26463045Sav145390 	}
26471772Sjl139090 
26482214Sav145390 	mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL);
26492214Sav145390 
26502214Sav145390 	for (i = 0; i < len1 / sizeof (cs_status_t); i++) {
26512214Sav145390 		nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) |
26525080Swh31274 		    ((uint64_t)cs_status[i].cs_avail_low);
26532214Sav145390 	}
26542214Sav145390 	if (len1 > 0)
26552214Sav145390 		kmem_free(cs_status, len1);
26562214Sav145390 	nbanks = len / sizeof (struct mc_addr_spec);
26572214Sav145390 
26582214Sav145390 	if (nbanks > 0)
26592214Sav145390 		nbytes /= nbanks;
26602214Sav145390 	else {
26612214Sav145390 		/* No need to free macaddr because len must be 0 */
26622214Sav145390 		mcp->mc_status |= MC_MEMORYLESS;
26632214Sav145390 		return (DDI_SUCCESS);
26642214Sav145390 	}
26652214Sav145390 
26662214Sav145390 	for (i = 0; i < BANKNUM_PER_SB; i++) {
26672214Sav145390 		mcp->mc_scf_retry[i] = 0;
26682214Sav145390 		mcp->mc_period[i] = 0;
26692214Sav145390 		mcp->mc_speedup_period[i] = 0;
26702214Sav145390 	}
26712214Sav145390 
26722214Sav145390 	/*
26732214Sav145390 	 * Get the memory size here. Let it be B (bytes).
26742214Sav145390 	 * Let T be the time in u.s. to scan 64 bytes.
26752214Sav145390 	 * If we want to complete 1 round of scanning in P seconds.
26762214Sav145390 	 *
26772214Sav145390 	 *	B * T * 10^(-6)	= P
26782214Sav145390 	 *	---------------
26792214Sav145390 	 *		64
26802214Sav145390 	 *
26812214Sav145390 	 *	T = P * 64 * 10^6
26822214Sav145390 	 *	    -------------
26832214Sav145390 	 *		B
26842214Sav145390 	 *
26852214Sav145390 	 *	  = P * 64 * 10^6
26862214Sav145390 	 *	    -------------
26872214Sav145390 	 *		B
26882214Sav145390 	 *
26892214Sav145390 	 *	The timing bits are set in PTRL_CNTL[28:26] where
26902214Sav145390 	 *
26912214Sav145390 	 *	0	- 1 m.s
26922214Sav145390 	 *	1	- 512 u.s.
26932214Sav145390 	 *	10	- 256 u.s.
26942214Sav145390 	 *	11	- 128 u.s.
26952214Sav145390 	 *	100	- 64 u.s.
26962214Sav145390 	 *	101	- 32 u.s.
26972214Sav145390 	 *	110	- 0 u.s.
26982214Sav145390 	 *	111	- reserved.
26992214Sav145390 	 *
27002214Sav145390 	 *
27012214Sav145390 	 *	a[0] = 110, a[1] = 101, ... a[6] = 0
27022214Sav145390 	 *
27032214Sav145390 	 *	cs-status property is int x 7
27042214Sav145390 	 *	0 - cs#
27052214Sav145390 	 *	1 - cs-status
27062214Sav145390 	 *	2 - cs-avail.hi
27072214Sav145390 	 *	3 - cs-avail.lo
27082214Sav145390 	 *	4 - dimm-capa.hi
27092214Sav145390 	 *	5 - dimm-capa.lo
27102214Sav145390 	 *	6 - #of dimms
27112214Sav145390 	 */
27122214Sav145390 
27132214Sav145390 	if (nbytes > 0) {
27142214Sav145390 		int i;
27152214Sav145390 		uint64_t ms;
27162214Sav145390 		ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes;
27172214Sav145390 		mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds;
27182214Sav145390 		for (i = 0; i < MC_MAX_SPEEDS - 1; i++) {
27192214Sav145390 			if (ms < mc_scan_speeds[i + 1].mc_period) {
27202214Sav145390 				mcp->mc_speed = mc_scan_speeds[i].mc_speeds;
27212214Sav145390 				break;
27222214Sav145390 			}
27232214Sav145390 		}
27242214Sav145390 	} else
27252214Sav145390 		mcp->mc_speed = 0;
27262214Sav145390 
27272214Sav145390 
27281772Sjl139090 	for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) {
27291772Sjl139090 		struct mc_bank *bankp;
27305310Sdhain 		mc_retry_info_t *retry;
27311772Sjl139090 		uint32_t reg;
27325310Sdhain 		int k;
27331772Sjl139090 
27341772Sjl139090 		/*
27351772Sjl139090 		 * setup bank
27361772Sjl139090 		 */
27371772Sjl139090 		bk = macaddr[i].bank;
27381772Sjl139090 		bankp = &(mcp->mc_bank[bk]);
27391772Sjl139090 		bankp->mcb_status = BANK_INSTALLED;
27401772Sjl139090 		bankp->mcb_reg_base = REGS_PA(macaddr, i);
27411772Sjl139090 
27425310Sdhain 		bankp->mcb_retry_freelist = NULL;
27435310Sdhain 		bankp->mcb_retry_pending = NULL;
27445310Sdhain 		bankp->mcb_active = NULL;
27455310Sdhain 		retry = &bankp->mcb_retry_infos[0];
27465310Sdhain 		for (k = 0; k < MC_RETRY_COUNT; k++, retry++) {
27475310Sdhain 			mc_retry_info_put(&bankp->mcb_retry_freelist, retry);
27485310Sdhain 		}
27495310Sdhain 
27501772Sjl139090 		reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk));
27511772Sjl139090 		bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS);
27521772Sjl139090 
27531772Sjl139090 		/*
27541772Sjl139090 		 * check if mirror mode
27551772Sjl139090 		 */
27561772Sjl139090 		mirr = LD_MAC_REG(MAC_MIRR(mcp, bk));
27571772Sjl139090 
27581772Sjl139090 		if (mirr & MAC_MIRR_MIRROR_MODE) {
27595080Swh31274 			MC_LOG("Mirror -> /LSB%d/B%d\n", mcp->mc_board_num,
27605080Swh31274 			    bk);
27611772Sjl139090 			bankp->mcb_status |= BANK_MIRROR_MODE;
27625080Swh31274 			mirror_mode = 1;
27631772Sjl139090 			/*
27641772Sjl139090 			 * The following bit is only used for
27651772Sjl139090 			 * error injection.  We should clear it
27661772Sjl139090 			 */
27671772Sjl139090 			if (mirr & MAC_MIRR_BANK_EXCLUSIVE)
27685080Swh31274 				ST_MAC_REG(MAC_MIRR(mcp, bk), 0);
27691772Sjl139090 		}
27701772Sjl139090 
27711772Sjl139090 		/*
27721772Sjl139090 		 * restart if not mirror mode or the other bank
27731772Sjl139090 		 * of the mirror is not running
27741772Sjl139090 		 */
27751772Sjl139090 		if (!(mirr & MAC_MIRR_MIRROR_MODE) ||
27765080Swh31274 		    !(mcp->mc_bank[bk^1].mcb_status & BANK_PTRL_RUNNING)) {
27775080Swh31274 			MC_LOG("Starting up /LSB%d/B%d\n", mcp->mc_board_num,
27785080Swh31274 			    bk);
27792662Shyw 			get_ptrl_start_address(mcp, bk, &rsaddr.mi_restartaddr);
27802662Shyw 			rsaddr.mi_valid = 0;
27812662Shyw 			rsaddr.mi_injectrestart = 0;
27822662Shyw 			restart_patrol(mcp, bk, &rsaddr);
27831772Sjl139090 		} else {
27841772Sjl139090 			MC_LOG("Not starting up /LSB%d/B%d\n",
27855080Swh31274 			    mcp->mc_board_num, bk);
27861772Sjl139090 		}
27871772Sjl139090 		bankp->mcb_status |= BANK_PTRL_RUNNING;
27881772Sjl139090 	}
27892214Sav145390 	if (len > 0)
27902214Sav145390 		kmem_free(macaddr, len);
27912214Sav145390 
27925080Swh31274 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, mcp->mc_dip, "mirror-mode",
27935080Swh31274 	    mirror_mode);
27945080Swh31274 	if (ret != DDI_PROP_SUCCESS) {
27955080Swh31274 		cmn_err(CE_WARN, "Unable to update mirror-mode property");
27965080Swh31274 	}
27975080Swh31274 
27982214Sav145390 	mcp->mc_dimm_list = mc_get_dimm_list(mcp);
27991772Sjl139090 
28001772Sjl139090 	/*
28011772Sjl139090 	 * set interval in HZ.
28021772Sjl139090 	 */
28031772Sjl139090 	mcp->mc_last_error = 0;
28042214Sav145390 
28051772Sjl139090 	/* restart memory patrol checking */
28061772Sjl139090 	mcp->mc_status |= MC_POLL_RUNNING;
28071772Sjl139090 
28081772Sjl139090 	return (DDI_SUCCESS);
28091772Sjl139090 }
28101772Sjl139090 
28111772Sjl139090 int
28121772Sjl139090 mc_board_del(mc_opl_t *mcp)
28131772Sjl139090 {
28141772Sjl139090 	int i;
28151772Sjl139090 	scf_log_t *p;
28161772Sjl139090 
28171772Sjl139090 	/*
28181772Sjl139090 	 * cleanup mac state
28191772Sjl139090 	 */
28201772Sjl139090 	mutex_enter(&mcp->mc_lock);
28212214Sav145390 	if (mcp->mc_status & MC_MEMORYLESS) {
28222214Sav145390 		mutex_exit(&mcp->mc_lock);
28232214Sav145390 		mutex_destroy(&mcp->mc_lock);
28242214Sav145390 		return (DDI_SUCCESS);
28252214Sav145390 	}
28261772Sjl139090 	for (i = 0; i < BANKNUM_PER_SB; i++) {
28271772Sjl139090 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
28281772Sjl139090 			mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED;
28291772Sjl139090 		}
28301772Sjl139090 	}
28311772Sjl139090 
28321772Sjl139090 	/* stop memory patrol checking */
28332662Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
28341772Sjl139090 
28351772Sjl139090 	/* just throw away all the scf logs */
28362214Sav145390 	for (i = 0; i < BANKNUM_PER_SB; i++) {
28375080Swh31274 		while ((p = mcp->mc_scf_log[i]) != NULL) {
28385080Swh31274 			mcp->mc_scf_log[i] = p->sl_next;
28395080Swh31274 			mcp->mc_scf_total[i]--;
28405080Swh31274 			kmem_free(p, sizeof (scf_log_t));
28415080Swh31274 		}
28421772Sjl139090 	}
28431772Sjl139090 
28441772Sjl139090 	if (mcp->mlist)
28451772Sjl139090 		mc_memlist_delete(mcp->mlist);
28461772Sjl139090 
28472214Sav145390 	if (mcp->mc_dimm_list)
28482214Sav145390 		mc_free_dimm_list(mcp->mc_dimm_list);
28492214Sav145390 
28501772Sjl139090 	mutex_exit(&mcp->mc_lock);
28511772Sjl139090 
28521772Sjl139090 	mutex_destroy(&mcp->mc_lock);
28531772Sjl139090 	return (DDI_SUCCESS);
28541772Sjl139090 }
28551772Sjl139090 
28561772Sjl139090 int
28571772Sjl139090 mc_suspend(mc_opl_t *mcp, uint32_t flag)
28581772Sjl139090 {
28591772Sjl139090 	/* stop memory patrol checking */
28601772Sjl139090 	mutex_enter(&mcp->mc_lock);
28612214Sav145390 	if (mcp->mc_status & MC_MEMORYLESS) {
28622214Sav145390 		mutex_exit(&mcp->mc_lock);
28632214Sav145390 		return (DDI_SUCCESS);
28642214Sav145390 	}
28652214Sav145390 
28662662Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
28672662Shyw 
28681772Sjl139090 	mcp->mc_status |= flag;
28691772Sjl139090 	mutex_exit(&mcp->mc_lock);
28701772Sjl139090 
28711772Sjl139090 	return (DDI_SUCCESS);
28721772Sjl139090 }
28731772Sjl139090 
28743354Sjl139090 void
28753354Sjl139090 opl_mc_update_mlist(void)
28763354Sjl139090 {
28773354Sjl139090 	int i;
28783354Sjl139090 	mc_opl_t *mcp;
28793354Sjl139090 
28803354Sjl139090 	/*
28813354Sjl139090 	 * memory information is not updated until
28823354Sjl139090 	 * the post attach/detach stage during DR.
28833354Sjl139090 	 * This interface is used by dr_mem to inform
28843354Sjl139090 	 * mc-opl to update the mlist.
28853354Sjl139090 	 */
28863354Sjl139090 
28873354Sjl139090 	mutex_enter(&mcmutex);
28883354Sjl139090 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
28893354Sjl139090 		if ((mcp = mc_instances[i]) == NULL)
28903354Sjl139090 			continue;
28913354Sjl139090 		mutex_enter(&mcp->mc_lock);
28923354Sjl139090 		if (mcp->mlist)
28933354Sjl139090 			mc_memlist_delete(mcp->mlist);
28943354Sjl139090 		mcp->mlist = NULL;
28953354Sjl139090 		mc_get_mlist(mcp);
28963354Sjl139090 		mutex_exit(&mcp->mc_lock);
28973354Sjl139090 	}
28983354Sjl139090 	mutex_exit(&mcmutex);
28993354Sjl139090 }
29003354Sjl139090 
29011772Sjl139090 /* caller must clear the SUSPEND bits or this will do nothing */
29021772Sjl139090 
29031772Sjl139090 int
29041772Sjl139090 mc_resume(mc_opl_t *mcp, uint32_t flag)
29051772Sjl139090 {
29061772Sjl139090 	int i;
29071772Sjl139090 	uint64_t basepa;
29081772Sjl139090 
29091772Sjl139090 	mutex_enter(&mcp->mc_lock);
29102214Sav145390 	if (mcp->mc_status & MC_MEMORYLESS) {
29112214Sav145390 		mutex_exit(&mcp->mc_lock);
29122214Sav145390 		return (DDI_SUCCESS);
29132214Sav145390 	}
29141772Sjl139090 	basepa = mcp->mc_start_address;
29151772Sjl139090 	if (get_base_address(mcp) == DDI_FAILURE) {
29161772Sjl139090 		mutex_exit(&mcp->mc_lock);
29171772Sjl139090 		return (DDI_FAILURE);
29181772Sjl139090 	}
29191772Sjl139090 
29201772Sjl139090 	if (basepa != mcp->mc_start_address) {
29211772Sjl139090 		if (mcp->mlist)
29221772Sjl139090 			mc_memlist_delete(mcp->mlist);
29231772Sjl139090 		mcp->mlist = NULL;
29241772Sjl139090 		mc_get_mlist(mcp);
29251772Sjl139090 	}
29261772Sjl139090 
29271772Sjl139090 	mcp->mc_status &= ~flag;
29281772Sjl139090 
29291772Sjl139090 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
29301772Sjl139090 		mutex_exit(&mcp->mc_lock);
29311772Sjl139090 		return (DDI_SUCCESS);
29321772Sjl139090 	}
29331772Sjl139090 
29341772Sjl139090 	if (!(mcp->mc_status & MC_POLL_RUNNING)) {
29351772Sjl139090 		/* restart memory patrol checking */
29361772Sjl139090 		mcp->mc_status |= MC_POLL_RUNNING;
29371772Sjl139090 		for (i = 0; i < BANKNUM_PER_SB; i++) {
29381772Sjl139090 			if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
29395310Sdhain 				mc_check_errors_func(mcp);
29401772Sjl139090 			}
29411772Sjl139090 		}
29421772Sjl139090 	}
29431772Sjl139090 	mutex_exit(&mcp->mc_lock);
29441772Sjl139090 
29451772Sjl139090 	return (DDI_SUCCESS);
29461772Sjl139090 }
29471772Sjl139090 
29481772Sjl139090 static mc_opl_t *
29491772Sjl139090 mc_pa_to_mcp(uint64_t pa)
29501772Sjl139090 {
29512214Sav145390 	mc_opl_t *mcp;
29522214Sav145390 	int i;
29532214Sav145390 
29541772Sjl139090 	ASSERT(MUTEX_HELD(&mcmutex));
29552214Sav145390 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
29562214Sav145390 		if ((mcp = mc_instances[i]) == NULL)
29572214Sav145390 			continue;
29581772Sjl139090 		/* if mac patrol is suspended, we cannot rely on it */
29592214Sav145390 		if (!(mcp->mc_status & MC_POLL_RUNNING) ||
29605080Swh31274 		    (mcp->mc_status & MC_SOFT_SUSPENDED))
29611772Sjl139090 			continue;
29622662Shyw 		if (mc_rangecheck_pa(mcp, pa)) {
29632214Sav145390 			return (mcp);
29641772Sjl139090 		}
29651772Sjl139090 	}
29661772Sjl139090 	return (NULL);
29671772Sjl139090 }
29681772Sjl139090 
29691772Sjl139090 /*
29701772Sjl139090  * Get Physical Board number from Logical one.
29711772Sjl139090  */
29721772Sjl139090 static int
29731772Sjl139090 mc_opl_get_physical_board(int sb)
29741772Sjl139090 {
29751772Sjl139090 	if (&opl_get_physical_board) {
29761772Sjl139090 		return (opl_get_physical_board(sb));
29771772Sjl139090 	}
29781772Sjl139090 
29791772Sjl139090 	cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n");
29801772Sjl139090 	return (-1);
29811772Sjl139090 }
29821772Sjl139090 
29831772Sjl139090 /* ARGSUSED */
29841772Sjl139090 int
29851772Sjl139090 mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen,
29861772Sjl139090 	int *lenp)
29871772Sjl139090 {
29882214Sav145390 	int i;
29893045Sav145390 	int j;
29902214Sav145390 	int sb;
29911772Sjl139090 	int bank;
29923045Sav145390 	int cs;
29936297Sjl139090 	int rv = 0;
29942214Sav145390 	mc_opl_t *mcp;
29952214Sav145390 	char memb_num;
29961772Sjl139090 
29971772Sjl139090 	mutex_enter(&mcmutex);
29981772Sjl139090 
29991772Sjl139090 	if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) ||
30005080Swh31274 	    (!pa_is_valid(mcp, flt_addr))) {
30011772Sjl139090 		mutex_exit(&mcmutex);
30021772Sjl139090 		if (snprintf(buf, buflen, "UNKNOWN") >= buflen) {
30031772Sjl139090 			return (ENOSPC);
30041772Sjl139090 		} else {
30051772Sjl139090 			if (lenp)
30061772Sjl139090 				*lenp = strlen(buf);
30071772Sjl139090 		}
30081772Sjl139090 		return (0);
30091772Sjl139090 	}
30101772Sjl139090 
30111772Sjl139090 	bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address);
30123045Sav145390 	sb = mcp->mc_phys_board_num;
30133045Sav145390 	cs = pa_to_cs(mcp, flt_addr - mcp->mc_start_address);
30141772Sjl139090 
30151772Sjl139090 	if (sb == -1) {
30161772Sjl139090 		mutex_exit(&mcmutex);
30171772Sjl139090 		return (ENXIO);
30181772Sjl139090 	}
30191772Sjl139090 
30206297Sjl139090 	switch (plat_model) {
30216297Sjl139090 	case MODEL_DC:
30222214Sav145390 		i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
30233045Sav145390 		j = (cs == 0) ? i : i + 2;
30243045Sav145390 		snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
30252214Sav145390 		    model_names[plat_model].unit_name, sb,
30263045Sav145390 		    mc_dc_dimm_unum_table[j],
30273045Sav145390 		    mc_dc_dimm_unum_table[j + 1]);
30286297Sjl139090 		break;
30296297Sjl139090 	case MODEL_FF2:
30306297Sjl139090 	case MODEL_FF1:
30312214Sav145390 		i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
30323045Sav145390 		j = (cs == 0) ? i : i + 2;
30332214Sav145390 		memb_num = mc_ff_dimm_unum_table[i][0];
30343045Sav145390 		snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s",
30352214Sav145390 		    model_names[plat_model].unit_name,
30362214Sav145390 		    model_names[plat_model].mem_name, memb_num,
30373045Sav145390 		    &mc_ff_dimm_unum_table[j][1],
30383045Sav145390 		    &mc_ff_dimm_unum_table[j + 1][1]);
30396297Sjl139090 		break;
30406297Sjl139090 	case MODEL_IKKAKU:
30416297Sjl139090 		i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
30426297Sjl139090 		j = (cs == 0) ? i : i + 2;
30436297Sjl139090 		snprintf(buf, buflen, "/%s/MEM%s MEM%s",
30446297Sjl139090 		    model_names[plat_model].unit_name,
30456297Sjl139090 		    &mc_ff_dimm_unum_table[j][1],
30466297Sjl139090 		    &mc_ff_dimm_unum_table[j + 1][1]);
30476297Sjl139090 		break;
30486297Sjl139090 	default:
30496297Sjl139090 		rv = ENXIO;
30502214Sav145390 	}
30512214Sav145390 	if (lenp) {
30522214Sav145390 		*lenp = strlen(buf);
30531772Sjl139090 	}
30541772Sjl139090 	mutex_exit(&mcmutex);
30556297Sjl139090 	return (rv);
30561772Sjl139090 }
30571772Sjl139090 
30581772Sjl139090 int
30592214Sav145390 opl_mc_suspend(void)
30601772Sjl139090 {
30611772Sjl139090 	mc_opl_t *mcp;
30622214Sav145390 	int i;
30631772Sjl139090 
30641772Sjl139090 	mutex_enter(&mcmutex);
30652214Sav145390 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
30662214Sav145390 		if ((mcp = mc_instances[i]) == NULL)
30672214Sav145390 			continue;
30682214Sav145390 		mc_suspend(mcp, MC_SOFT_SUSPENDED);
30691772Sjl139090 	}
30701772Sjl139090 	mutex_exit(&mcmutex);
30712214Sav145390 
30721772Sjl139090 	return (0);
30731772Sjl139090 }
30741772Sjl139090 
30751772Sjl139090 int
30762214Sav145390 opl_mc_resume(void)
30771772Sjl139090 {
30781772Sjl139090 	mc_opl_t *mcp;
30792214Sav145390 	int i;
30801772Sjl139090 
30811772Sjl139090 	mutex_enter(&mcmutex);
30822214Sav145390 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
30832214Sav145390 		if ((mcp = mc_instances[i]) == NULL)
30842214Sav145390 			continue;
30852214Sav145390 		mc_resume(mcp, MC_SOFT_SUSPENDED);
30861772Sjl139090 	}
30871772Sjl139090 	mutex_exit(&mcmutex);
30882214Sav145390 
30891772Sjl139090 	return (0);
30901772Sjl139090 }
30911772Sjl139090 static void
30921772Sjl139090 insert_mcp(mc_opl_t *mcp)
30931772Sjl139090 {
30941772Sjl139090 	mutex_enter(&mcmutex);
30952214Sav145390 	if (mc_instances[mcp->mc_board_num] != NULL) {
30962214Sav145390 		MC_LOG("mc-opl instance for board# %d already exists\n",
30975080Swh31274 		    mcp->mc_board_num);
30982214Sav145390 	}
30992214Sav145390 	mc_instances[mcp->mc_board_num] = mcp;
31001772Sjl139090 	mutex_exit(&mcmutex);
31011772Sjl139090 }
31021772Sjl139090 
31031772Sjl139090 static void
31041772Sjl139090 delete_mcp(mc_opl_t *mcp)
31051772Sjl139090 {
31062214Sav145390 	mutex_enter(&mcmutex);
31072214Sav145390 	mc_instances[mcp->mc_board_num] = 0;
31082214Sav145390 	mutex_exit(&mcmutex);
31091772Sjl139090 }
31101772Sjl139090 
31111772Sjl139090 /* Error injection interface */
31121772Sjl139090 
31132494Shyw static void
31142494Shyw mc_lock_va(uint64_t pa, caddr_t new_va)
31152494Shyw {
31162494Shyw 	tte_t tte;
31172494Shyw 
31182662Shyw 	vtag_flushpage(new_va, (uint64_t)ksfmmup);
31195080Swh31274 	sfmmu_memtte(&tte, pa >> PAGESHIFT, PROC_DATA|HAT_NOSYNC, TTE8K);
31202494Shyw 	tte.tte_intlo |= TTE_LCK_INT;
31212494Shyw 	sfmmu_dtlb_ld_kva(new_va, &tte);
31222494Shyw }
31232494Shyw 
31242494Shyw static void
31252494Shyw mc_unlock_va(caddr_t va)
31262494Shyw {
31272494Shyw 	vtag_flushpage(va, (uint64_t)ksfmmup);
31282494Shyw }
31292494Shyw 
31301772Sjl139090 /* ARGSUSED */
31311772Sjl139090 int
31321772Sjl139090 mc_inject_error(int error_type, uint64_t pa, uint32_t flags)
31331772Sjl139090 {
31341772Sjl139090 	mc_opl_t *mcp;
31351772Sjl139090 	int bank;
31361772Sjl139090 	uint32_t dimm_addr;
31371772Sjl139090 	uint32_t cntl;
31382662Shyw 	mc_rsaddr_info_t rsaddr;
31391772Sjl139090 	uint32_t data, stat;
31401772Sjl139090 	int both_sides = 0;
31411772Sjl139090 	uint64_t pa0;
31422494Shyw 	int extra_injection_needed = 0;
31431772Sjl139090 	extern void cpu_flush_ecache(void);
31441772Sjl139090 
31451772Sjl139090 	MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags);
31461772Sjl139090 
31471772Sjl139090 	mutex_enter(&mcmutex);
31481772Sjl139090 	if ((mcp = mc_pa_to_mcp(pa)) == NULL) {
31491772Sjl139090 		mutex_exit(&mcmutex);
31501772Sjl139090 		MC_LOG("mc_inject_error: invalid pa\n");
31511772Sjl139090 		return (ENOTSUP);
31521772Sjl139090 	}
31531772Sjl139090 
31541772Sjl139090 	mutex_enter(&mcp->mc_lock);
31551772Sjl139090 	mutex_exit(&mcmutex);
31561772Sjl139090 
31571772Sjl139090 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
31581772Sjl139090 		mutex_exit(&mcp->mc_lock);
31591772Sjl139090 		MC_LOG("mc-opl has been suspended.  No error injection.\n");
31601772Sjl139090 		return (EBUSY);
31611772Sjl139090 	}
31621772Sjl139090 
31631772Sjl139090 	/* convert pa to offset within the board */
31641772Sjl139090 	MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address);
31651772Sjl139090 
31661772Sjl139090 	if (!pa_is_valid(mcp, pa)) {
31671772Sjl139090 		mutex_exit(&mcp->mc_lock);
31681772Sjl139090 		return (EINVAL);
31691772Sjl139090 	}
31701772Sjl139090 
31711772Sjl139090 	pa0 = pa - mcp->mc_start_address;
31721772Sjl139090 
31731772Sjl139090 	bank = pa_to_bank(mcp, pa0);
31741772Sjl139090 
31751772Sjl139090 	if (flags & MC_INJECT_FLAG_OTHER)
31761772Sjl139090 		bank = bank ^ 1;
31771772Sjl139090 
31781772Sjl139090 	if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) {
31791772Sjl139090 		mutex_exit(&mcp->mc_lock);
31801772Sjl139090 		MC_LOG("Not mirror mode\n");
31811772Sjl139090 		return (EINVAL);
31821772Sjl139090 	}
31831772Sjl139090 
31841772Sjl139090 	dimm_addr = pa_to_dimm(mcp, pa0);
31851772Sjl139090 
31865080Swh31274 	MC_LOG("injecting error to /LSB%d/B%d/%x\n", mcp->mc_board_num, bank,
31875080Swh31274 	    dimm_addr);
31881772Sjl139090 
31891772Sjl139090 
31901772Sjl139090 	switch (error_type) {
31911772Sjl139090 	case MC_INJECT_INTERMITTENT_MCE:
31921772Sjl139090 	case MC_INJECT_PERMANENT_MCE:
31931772Sjl139090 	case MC_INJECT_MUE:
31941772Sjl139090 		both_sides = 1;
31951772Sjl139090 	}
31961772Sjl139090 
31971772Sjl139090 	if (flags & MC_INJECT_FLAG_RESET)
31981772Sjl139090 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0);
31991772Sjl139090 
32001772Sjl139090 	ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK);
32011772Sjl139090 
32021772Sjl139090 	if (both_sides) {
32031772Sjl139090 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0);
32045080Swh31274 		ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), dimm_addr &
32055080Swh31274 		    MAC_EG_ADD_MASK);
32061772Sjl139090 	}
32071772Sjl139090 
32081772Sjl139090 	switch (error_type) {
32092494Shyw 	case MC_INJECT_SUE:
32102494Shyw 		extra_injection_needed = 1;
32112494Shyw 		/*FALLTHROUGH*/
32121772Sjl139090 	case MC_INJECT_UE:
32131772Sjl139090 	case MC_INJECT_MUE:
32141772Sjl139090 		if (flags & MC_INJECT_FLAG_PATH) {
32155080Swh31274 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 |
32165080Swh31274 			    MAC_EG_FORCE_READ16 | MAC_EG_RDERR_ONCE;
32171772Sjl139090 		} else {
32185080Swh31274 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR00 |
32195080Swh31274 			    MAC_EG_FORCE_DERR16 | MAC_EG_DERR_ONCE;
32201772Sjl139090 		}
32211772Sjl139090 		flags |= MC_INJECT_FLAG_ST;
32221772Sjl139090 		break;
32231772Sjl139090 	case MC_INJECT_INTERMITTENT_CE:
32241772Sjl139090 	case MC_INJECT_INTERMITTENT_MCE:
32251772Sjl139090 		if (flags & MC_INJECT_FLAG_PATH) {
32265080Swh31274 			cntl = MAC_EG_ADD_FIX |MAC_EG_FORCE_READ00 |
32275080Swh31274 			    MAC_EG_RDERR_ONCE;
32281772Sjl139090 		} else {
32295080Swh31274 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 |
32305080Swh31274 			    MAC_EG_DERR_ONCE;
32311772Sjl139090 		}
32322494Shyw 		extra_injection_needed = 1;
32331772Sjl139090 		flags |= MC_INJECT_FLAG_ST;
32341772Sjl139090 		break;
32351772Sjl139090 	case MC_INJECT_PERMANENT_CE:
32361772Sjl139090 	case MC_INJECT_PERMANENT_MCE:
32371772Sjl139090 		if (flags & MC_INJECT_FLAG_PATH) {
32385080Swh31274 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 |
32395080Swh31274 			    MAC_EG_RDERR_ALWAYS;
32401772Sjl139090 		} else {
32415080Swh31274 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 |
32425080Swh31274 			    MAC_EG_DERR_ALWAYS;
32431772Sjl139090 		}
32441772Sjl139090 		flags |= MC_INJECT_FLAG_ST;
32451772Sjl139090 		break;
32461772Sjl139090 	case MC_INJECT_CMPE:
32471772Sjl139090 		data = 0xabcdefab;
32481772Sjl139090 		stphys(pa, data);
32491772Sjl139090 		cpu_flush_ecache();
32501772Sjl139090 		MC_LOG("CMPE: writing data %x to %lx\n", data, pa);
32511772Sjl139090 		ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE);
32521772Sjl139090 		stphys(pa, data ^ 0xffffffff);
32532662Shyw 		membar_sync();
32541772Sjl139090 		cpu_flush_ecache();
32551772Sjl139090 		ST_MAC_REG(MAC_MIRR(mcp, bank), 0);
32561772Sjl139090 		MC_LOG("CMPE: write new data %xto %lx\n", data, pa);
32571772Sjl139090 		cntl = 0;
32581772Sjl139090 		break;
32591772Sjl139090 	case MC_INJECT_NOP:
32601772Sjl139090 		cntl = 0;
32611772Sjl139090 		break;
32621772Sjl139090 	default:
32631772Sjl139090 		MC_LOG("mc_inject_error: invalid option\n");
32641772Sjl139090 		cntl = 0;
32651772Sjl139090 	}
32661772Sjl139090 
32671772Sjl139090 	if (cntl) {
32681772Sjl139090 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK);
32691772Sjl139090 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
32701772Sjl139090 
32711772Sjl139090 		if (both_sides) {
32721772Sjl139090 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
32735080Swh31274 			    MAC_EG_SETUP_MASK);
32741772Sjl139090 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
32751772Sjl139090 		}
32761772Sjl139090 	}
32771772Sjl139090 
32781772Sjl139090 	/*
32791772Sjl139090 	 * For all injection cases except compare error, we
32801772Sjl139090 	 * must write to the PA to trigger the error.
32811772Sjl139090 	 */
32821772Sjl139090 
32831772Sjl139090 	if (flags & MC_INJECT_FLAG_ST) {
32841772Sjl139090 		data = 0xf0e0d0c0;
32851772Sjl139090 		MC_LOG("Writing %x to %lx\n", data, pa);
32861772Sjl139090 		stphys(pa, data);
32871772Sjl139090 		cpu_flush_ecache();
32881772Sjl139090 	}
32891772Sjl139090 
32901772Sjl139090 
32911772Sjl139090 	if (flags & MC_INJECT_FLAG_LD) {
32922494Shyw 		if (flags & MC_INJECT_FLAG_PREFETCH) {
32932494Shyw 			/*
32942494Shyw 			 * Use strong prefetch operation to
32952494Shyw 			 * inject MI errors.
32962494Shyw 			 */
32972494Shyw 			page_t *pp;
32982494Shyw 			extern void mc_prefetch(caddr_t);
32992494Shyw 
33002494Shyw 			MC_LOG("prefetch\n");
33012494Shyw 
33022494Shyw 			pp = page_numtopp_nolock(pa >> PAGESHIFT);
33032494Shyw 			if (pp != NULL) {
33042494Shyw 				caddr_t	va, va1;
33052494Shyw 
33062494Shyw 				va = ppmapin(pp, PROT_READ|PROT_WRITE,
33075080Swh31274 				    (caddr_t)-1);
33082494Shyw 				kpreempt_disable();
33092494Shyw 				mc_lock_va((uint64_t)pa, va);
33102494Shyw 				va1 = va + (pa & (PAGESIZE - 1));
33112494Shyw 				mc_prefetch(va1);
33122494Shyw 				mc_unlock_va(va);
33132494Shyw 				kpreempt_enable();
33142494Shyw 				ppmapout(va);
33152494Shyw 
33162494Shyw 				/*
33172494Shyw 				 * For MI errors, we need one extra
33182494Shyw 				 * injection for HW patrol to stop.
33192494Shyw 				 */
33202494Shyw 				extra_injection_needed = 1;
33211772Sjl139090 			} else {
33222494Shyw 				cmn_err(CE_WARN, "Cannot find page structure"
33235080Swh31274 				    " for PA %lx\n", pa);
33241772Sjl139090 			}
33251772Sjl139090 		} else {
33261772Sjl139090 			MC_LOG("Reading from %lx\n", pa);
33271772Sjl139090 			data = ldphys(pa);
33281772Sjl139090 			MC_LOG("data = %x\n", data);
33291772Sjl139090 		}
33302494Shyw 
33312494Shyw 		if (extra_injection_needed) {
33322494Shyw 			/*
33332494Shyw 			 * These are the injection cases where the
33342494Shyw 			 * requested injected errors will not cause the HW
33352494Shyw 			 * patrol to stop. For these cases, we need to inject
33362494Shyw 			 * an extra 'real' PTRL error to force the
33372494Shyw 			 * HW patrol to stop so that we can report the
33382494Shyw 			 * errors injected. Note that we cannot read
33392494Shyw 			 * and report error status while the HW patrol
33402494Shyw 			 * is running.
33412494Shyw 			 */
33422494Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank),
33435080Swh31274 			    cntl & MAC_EG_SETUP_MASK);
33442494Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
33452494Shyw 
33462494Shyw 			if (both_sides) {
33475080Swh31274 				ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
33485080Swh31274 				    MAC_EG_SETUP_MASK);
33495080Swh31274 				ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
33502494Shyw 			}
33512494Shyw 			data = 0xf0e0d0c0;
33522494Shyw 			MC_LOG("Writing %x to %lx\n", data, pa);
33532494Shyw 			stphys(pa, data);
33542494Shyw 			cpu_flush_ecache();
33552494Shyw 		}
33561772Sjl139090 	}
33571772Sjl139090 
33581772Sjl139090 	if (flags & MC_INJECT_FLAG_RESTART) {
33591772Sjl139090 		MC_LOG("Restart patrol\n");
33602662Shyw 		rsaddr.mi_restartaddr.ma_bd = mcp->mc_board_num;
33612662Shyw 		rsaddr.mi_restartaddr.ma_bank = bank;
33622662Shyw 		rsaddr.mi_restartaddr.ma_dimm_addr = dimm_addr;
33632662Shyw 		rsaddr.mi_valid = 1;
33642662Shyw 		rsaddr.mi_injectrestart = 1;
33652662Shyw 		restart_patrol(mcp, bank, &rsaddr);
33661772Sjl139090 	}
33671772Sjl139090 
33681772Sjl139090 	if (flags & MC_INJECT_FLAG_POLL) {
33692214Sav145390 		int running;
33702867Shyw 		int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
33711772Sjl139090 
33721772Sjl139090 		MC_LOG("Poll patrol error\n");
33731772Sjl139090 		stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank));
33741772Sjl139090 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
33752214Sav145390 		running = cntl & MAC_CNTL_PTRL_START;
33762494Shyw 
33772494Shyw 		if (!running &&
33782494Shyw 		    (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS))) {
33792494Shyw 			/*
33802494Shyw 			 * HW patrol stopped and we have errors to
33812494Shyw 			 * report. Do it.
33822494Shyw 			 */
33832867Shyw 			mcp->mc_speedup_period[ebank] = 0;
33842662Shyw 			rsaddr.mi_valid = 0;
33852662Shyw 			rsaddr.mi_injectrestart = 0;
33862662Shyw 			if (IS_MIRROR(mcp, bank)) {
33872662Shyw 				mc_error_handler_mir(mcp, bank, &rsaddr);
33882662Shyw 			} else {
33892662Shyw 				mc_error_handler(mcp, bank, &rsaddr);
33902662Shyw 			}
33912662Shyw 
33922662Shyw 			restart_patrol(mcp, bank, &rsaddr);
33932494Shyw 		} else {
33942494Shyw 			/*
33952494Shyw 			 * We are expecting to report injected
33962494Shyw 			 * errors but the HW patrol is still running.
33972494Shyw 			 * Speed up the scanning
33982494Shyw 			 */
33992867Shyw 			mcp->mc_speedup_period[ebank] = 2;
34002494Shyw 			MAC_CMD(mcp, bank, 0);
34011772Sjl139090 			restart_patrol(mcp, bank, NULL);
34022494Shyw 		}
34031772Sjl139090 	}
34041772Sjl139090 
34051772Sjl139090 	mutex_exit(&mcp->mc_lock);
34061772Sjl139090 	return (0);
34071772Sjl139090 }
34082494Shyw 
34091772Sjl139090 void
34101772Sjl139090 mc_stphysio(uint64_t pa, uint32_t data)
34111772Sjl139090 {
34121772Sjl139090 	MC_LOG("0x%x -> pa(%lx)\n", data, pa);
34131772Sjl139090 	stphysio(pa, data);
34142214Sav145390 
34152214Sav145390 	/* force the above write to be processed by mac patrol */
34162494Shyw 	data = ldphysio(pa);
34172494Shyw 	MC_LOG("pa(%lx) = 0x%x\n", pa, data);
34181772Sjl139090 }
34191772Sjl139090 
34201772Sjl139090 uint32_t
34211772Sjl139090 mc_ldphysio(uint64_t pa)
34221772Sjl139090 {
34231772Sjl139090 	uint32_t rv;
34241772Sjl139090 
34251772Sjl139090 	rv = ldphysio(pa);
34261772Sjl139090 	MC_LOG("pa(%lx) = 0x%x\n", pa, rv);
34271772Sjl139090 	return (rv);
34281772Sjl139090 }
34292214Sav145390 
34302214Sav145390 #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
34312214Sav145390 
34322214Sav145390 /*
34332214Sav145390  * parse_unum_memory -- extract the board number and the DIMM name from
34342214Sav145390  * the unum.
34352214Sav145390  *
34362214Sav145390  * Return 0 for success and non-zero for a failure.
34372214Sav145390  */
34382214Sav145390 int
34392214Sav145390 parse_unum_memory(char *unum, int *board, char *dname)
34402214Sav145390 {
34412214Sav145390 	char *c;
34422214Sav145390 	char x, y, z;
34432214Sav145390 
34442214Sav145390 	if ((c = strstr(unum, "CMU")) != NULL) {
34452214Sav145390 		/* DC Model */
34462214Sav145390 		c += 3;
34472214Sav145390 		*board = (uint8_t)stoi(&c);
34482214Sav145390 		if ((c = strstr(c, "MEM")) == NULL) {
34492214Sav145390 			return (1);
34502214Sav145390 		}
34512214Sav145390 		c += 3;
34522214Sav145390 		if (strlen(c) < 3) {
34532214Sav145390 			return (2);
34542214Sav145390 		}
34552214Sav145390 		if ((!isdigit(c[0])) || (!(isdigit(c[1]))) ||
34562214Sav145390 		    ((c[2] != 'A') && (c[2] != 'B'))) {
34572214Sav145390 			return (3);
34582214Sav145390 		}
34592214Sav145390 		x = c[0];
34602214Sav145390 		y = c[1];
34612214Sav145390 		z = c[2];
34622214Sav145390 	} else if ((c = strstr(unum, "MBU_")) != NULL) {
34636297Sjl139090 		/*  FF1/FF2/Ikkaku Model */
34642214Sav145390 		c += 4;
34652214Sav145390 		if ((c[0] != 'A') && (c[0] != 'B')) {
34662214Sav145390 			return (4);
34672214Sav145390 		}
34686297Sjl139090 		if (plat_model == MODEL_IKKAKU) {
34696297Sjl139090 			/* Ikkaku Model */
34706297Sjl139090 			x = '0';
34716297Sjl139090 			*board = 0;
34726297Sjl139090 		} else {
34736297Sjl139090 			/* FF1/FF2 Model */
34746297Sjl139090 			if ((c = strstr(c, "MEMB")) == NULL) {
34756297Sjl139090 				return (5);
34766297Sjl139090 			}
34776297Sjl139090 			c += 4;
34786297Sjl139090 
34796297Sjl139090 			x = c[0];
34806297Sjl139090 			*board =  ((uint8_t)stoi(&c)) / 4;
34812214Sav145390 		}
34826297Sjl139090 
34832214Sav145390 		if ((c = strstr(c, "MEM")) == NULL) {
34842214Sav145390 			return (6);
34852214Sav145390 		}
34862214Sav145390 		c += 3;
34872214Sav145390 		if (strlen(c) < 2) {
34882214Sav145390 			return (7);
34892214Sav145390 		}
34902214Sav145390 		if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) {
34912214Sav145390 			return (8);
34922214Sav145390 		}
34932214Sav145390 		y = c[0];
34942214Sav145390 		z = c[1];
34952214Sav145390 	} else {
34962214Sav145390 		return (9);
34972214Sav145390 	}
34982214Sav145390 	if (*board < 0) {
34992214Sav145390 		return (10);
35002214Sav145390 	}
35012214Sav145390 	dname[0] = x;
35022214Sav145390 	dname[1] = y;
35032214Sav145390 	dname[2] = z;
35042214Sav145390 	dname[3] = '\0';
35052214Sav145390 	return (0);
35062214Sav145390 }
35072214Sav145390 
35082214Sav145390 /*
35092214Sav145390  * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and
35102214Sav145390  * the DIMM name.
35112214Sav145390  */
35122214Sav145390 int
35132214Sav145390 mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
35142214Sav145390     int buflen, int *lenp)
35152214Sav145390 {
35162214Sav145390 	int		ret = ENODEV;
35172214Sav145390 	mc_dimm_info_t	*d = NULL;
35182214Sav145390 
35192214Sav145390 	if ((d = mcp->mc_dimm_list) == NULL)
35202214Sav145390 		return (ENOTSUP);
35212214Sav145390 
35222214Sav145390 	for (; d != NULL; d = d->md_next) {
35232214Sav145390 		if (strcmp(d->md_dimmname, dname) == 0) {
35242214Sav145390 			break;
35252214Sav145390 		}
35262214Sav145390 	}
35272214Sav145390 	if (d != NULL) {
35282214Sav145390 		*lenp = strlen(d->md_serial) + strlen(d->md_partnum);
35292214Sav145390 		if (buflen <=  *lenp) {
35302214Sav145390 			cmn_err(CE_WARN, "mc_get_mem_sid_dimm: "
35312214Sav145390 			    "buflen is smaller than %d\n", *lenp);
35322214Sav145390 			ret = ENOSPC;
35332214Sav145390 		} else {
35342214Sav145390 			snprintf(buf, buflen, "%s:%s",
35352214Sav145390 			    d->md_serial, d->md_partnum);
35362214Sav145390 			ret = 0;
35372214Sav145390 		}
35382214Sav145390 	}
35392214Sav145390 	MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n",
35402214Sav145390 	    ret, dname, (ret == 0) ? buf : "");
35412214Sav145390 	return (ret);
35422214Sav145390 }
35432214Sav145390 
35442214Sav145390 int
35453045Sav145390 mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int sb,
35462214Sav145390     int bank, uint32_t mf_type, uint32_t d_slot)
35472214Sav145390 {
35482214Sav145390 	int	lenp = buflen;
35492214Sav145390 	int	id;
35502214Sav145390 	int	ret;
35512214Sav145390 	char	*dimmnm;
35522214Sav145390 
35535275Stsien 	if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
35545275Stsien 	    mf_type == FLT_TYPE_PERMANENT_CE) {
35552214Sav145390 		if (plat_model == MODEL_DC) {
35566297Sjl139090 			/*
35576297Sjl139090 			 * All DC models
35586297Sjl139090 			 */
35592214Sav145390 			id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
35603045Sav145390 			dimmnm = mc_dc_dimm_unum_table[id];
35612214Sav145390 		} else {
35626297Sjl139090 			/*
35636297Sjl139090 			 * All FF and Ikkaku models
35646297Sjl139090 			 */
35652214Sav145390 			id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
35663045Sav145390 			dimmnm = mc_ff_dimm_unum_table[id];
35672214Sav145390 		}
35682214Sav145390 		if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen,
35692214Sav145390 		    &lenp)) != 0) {
35702214Sav145390 			return (ret);
35712214Sav145390 		}
35722214Sav145390 	} else {
35732214Sav145390 		return (1);
35742214Sav145390 	}
35752214Sav145390 
35762214Sav145390 	return (0);
35772214Sav145390 }
35782214Sav145390 
35792214Sav145390 /*
35802214Sav145390  * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum.
35812214Sav145390  */
35822214Sav145390 int
35832214Sav145390 mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
35842214Sav145390 {
35852214Sav145390 	int	i;
35862214Sav145390 	int	ret = ENODEV;
35872214Sav145390 	int	board;
35882214Sav145390 	char	dname[MCOPL_MAX_DIMMNAME + 1];
35892214Sav145390 	mc_opl_t *mcp;
35902214Sav145390 
35912214Sav145390 	MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen);
35922214Sav145390 	if ((ret = parse_unum_memory(unum, &board, dname)) != 0) {
35932214Sav145390 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
35942214Sav145390 		    unum, ret);
35952214Sav145390 		return (EINVAL);
35962214Sav145390 	}
35972214Sav145390 
35982214Sav145390 	if (board < 0) {
35992214Sav145390 		MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n",
36002214Sav145390 		    board, dname);
36012214Sav145390 		return (EINVAL);
36022214Sav145390 	}
36032214Sav145390 
36042214Sav145390 	mutex_enter(&mcmutex);
36053152Sav145390 	/*
36063152Sav145390 	 * return ENOENT if we can not find the matching board.
36073152Sav145390 	 */
36083152Sav145390 	ret = ENOENT;
36092214Sav145390 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
36102214Sav145390 		if ((mcp = mc_instances[i]) == NULL)
36112214Sav145390 			continue;
36122214Sav145390 		mutex_enter(&mcp->mc_lock);
36133045Sav145390 		if (mcp->mc_phys_board_num != board) {
36143045Sav145390 			mutex_exit(&mcp->mc_lock);
36153045Sav145390 			continue;
36163045Sav145390 		}
36173045Sav145390 		ret = mc_get_mem_sid_dimm(mcp, dname, buf, buflen, lenp);
36183045Sav145390 		if (ret == 0) {
36192214Sav145390 			mutex_exit(&mcp->mc_lock);
36202214Sav145390 			break;
36212214Sav145390 		}
36222214Sav145390 		mutex_exit(&mcp->mc_lock);
36232214Sav145390 	}
36242214Sav145390 	mutex_exit(&mcmutex);
36252214Sav145390 	return (ret);
36262214Sav145390 }
36272214Sav145390 
36282214Sav145390 /*
36292214Sav145390  * mc_get_mem_offset -- get the offset in a DIMM for a given physical address.
36302214Sav145390  */
36312214Sav145390 int
36322214Sav145390 mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
36332214Sav145390 {
36342214Sav145390 	int		i;
36352214Sav145390 	int		ret = ENODEV;
36362214Sav145390 	mc_addr_t	maddr;
36372214Sav145390 	mc_opl_t	*mcp;
36382214Sav145390 
36392214Sav145390 	mutex_enter(&mcmutex);
36402501Sraghuram 	for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) {
36412214Sav145390 		if ((mcp = mc_instances[i]) == NULL)
36422214Sav145390 			continue;
36432214Sav145390 		mutex_enter(&mcp->mc_lock);
36442214Sav145390 		if (!pa_is_valid(mcp, paddr)) {
36452214Sav145390 			mutex_exit(&mcp->mc_lock);
36462214Sav145390 			continue;
36472214Sav145390 		}
36482214Sav145390 		if (pa_to_maddr(mcp, paddr, &maddr) == 0) {
36492214Sav145390 			*offp = maddr.ma_dimm_addr;
36502214Sav145390 			ret = 0;
36512214Sav145390 		}
36522214Sav145390 		mutex_exit(&mcp->mc_lock);
36532214Sav145390 	}
36542214Sav145390 	mutex_exit(&mcmutex);
36552214Sav145390 	MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n",
36562214Sav145390 	    ret, paddr, *offp);
36572214Sav145390 	return (ret);
36582214Sav145390 }
36592214Sav145390 
36602214Sav145390 /*
36612214Sav145390  * dname_to_bankslot - Get the bank and slot number from the DIMM name.
36622214Sav145390  */
36632214Sav145390 int
36642214Sav145390 dname_to_bankslot(char *dname, int *bank, int *slot)
36652214Sav145390 {
36662214Sav145390 	int i;
36672214Sav145390 	int tsz;
36682214Sav145390 	char **tbl;
36692214Sav145390 
36706297Sjl139090 	if (plat_model == MODEL_DC) {
36716297Sjl139090 		/*
36726297Sjl139090 		 * All DC models
36736297Sjl139090 		 */
36742214Sav145390 		tbl = mc_dc_dimm_unum_table;
36752214Sav145390 		tsz = OPL_MAX_DIMMS;
36762214Sav145390 	} else {
36776297Sjl139090 		/*
36786297Sjl139090 		 * All FF and Ikkaku models
36796297Sjl139090 		 */
36802214Sav145390 		tbl = mc_ff_dimm_unum_table;
36812214Sav145390 		tsz = 2 * OPL_MAX_DIMMS;
36822214Sav145390 	}
36832214Sav145390 
36842214Sav145390 	for (i = 0; i < tsz; i++) {
36852214Sav145390 		if (strcmp(dname,  tbl[i]) == 0) {
36862214Sav145390 			break;
36872214Sav145390 		}
36882214Sav145390 	}
36892214Sav145390 	if (i == tsz) {
36902214Sav145390 		return (1);
36912214Sav145390 	}
36922214Sav145390 	*bank = INDEX_TO_BANK(i);
36932214Sav145390 	*slot = INDEX_TO_SLOT(i);
36942214Sav145390 	return (0);
36952214Sav145390 }
36962214Sav145390 
36972214Sav145390 /*
36982214Sav145390  * mc_get_mem_addr -- get the physical address of a DIMM corresponding
36992214Sav145390  * to the unum and sid.
37002214Sav145390  */
37012214Sav145390 int
37022214Sav145390 mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr)
37032214Sav145390 {
37042214Sav145390 	int	board;
37052214Sav145390 	int	bank;
37062214Sav145390 	int	slot;
37072214Sav145390 	int	i;
37082214Sav145390 	int	ret = ENODEV;
37092214Sav145390 	char	dname[MCOPL_MAX_DIMMNAME + 1];
37102214Sav145390 	mc_addr_t maddr;
37112214Sav145390 	mc_opl_t *mcp;
37122214Sav145390 
37132214Sav145390 	MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n",
37142214Sav145390 	    unum, sid, offset);
37152214Sav145390 	if (parse_unum_memory(unum, &board, dname) != 0) {
37162214Sav145390 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
37172214Sav145390 		    unum, ret);
37182214Sav145390 		return (EINVAL);
37192214Sav145390 	}
37202214Sav145390 
37212214Sav145390 	if (board < 0) {
37222214Sav145390 		MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n",
37232214Sav145390 		    board, dname);
37242214Sav145390 		return (EINVAL);
37252214Sav145390 	}
37262214Sav145390 
37272214Sav145390 	mutex_enter(&mcmutex);
37282214Sav145390 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
37292214Sav145390 		if ((mcp = mc_instances[i]) == NULL)
37302214Sav145390 			continue;
37312214Sav145390 		mutex_enter(&mcp->mc_lock);
37323045Sav145390 		if (mcp->mc_phys_board_num != board) {
37332214Sav145390 			mutex_exit(&mcp->mc_lock);
37342214Sav145390 			continue;
37352214Sav145390 		}
37362214Sav145390 
37372214Sav145390 		ret = dname_to_bankslot(dname, &bank, &slot);
37382214Sav145390 		MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot);
37392214Sav145390 		if (ret != 0) {
37402214Sav145390 			MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n");
37412214Sav145390 			ret = ENODEV;
37422214Sav145390 		} else {
37433045Sav145390 			maddr.ma_bd = mcp->mc_board_num;
37442214Sav145390 			maddr.ma_bank =  bank;
37452214Sav145390 			maddr.ma_dimm_addr = offset;
37462214Sav145390 			ret = mcaddr_to_pa(mcp, &maddr, paddr);
37472214Sav145390 			if (ret != 0) {
37482214Sav145390 				MC_LOG("mc_get_mem_addr: "
37492214Sav145390 				    "mcaddr_to_pa failed\n");
37502214Sav145390 				ret = ENODEV;
3751*6693Swh31274 				mutex_exit(&mcp->mc_lock);
3752*6693Swh31274 				continue;
37532214Sav145390 			}
37543045Sav145390 			mutex_exit(&mcp->mc_lock);
37553045Sav145390 			break;
37562214Sav145390 		}
37572214Sav145390 		mutex_exit(&mcp->mc_lock);
37582214Sav145390 	}
37592214Sav145390 	mutex_exit(&mcmutex);
37602214Sav145390 	MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr);
37612214Sav145390 	return (ret);
37622214Sav145390 }
37632214Sav145390 
37642214Sav145390 static void
37652214Sav145390 mc_free_dimm_list(mc_dimm_info_t *d)
37662214Sav145390 {
37672214Sav145390 	mc_dimm_info_t *next;
37682214Sav145390 
37692214Sav145390 	while (d != NULL) {
37702214Sav145390 		next = d->md_next;
37712214Sav145390 		kmem_free(d, sizeof (mc_dimm_info_t));
37722214Sav145390 		d = next;
37732214Sav145390 	}
37742214Sav145390 }
37752214Sav145390 
37762214Sav145390 /*
37772214Sav145390  * mc_get_dimm_list -- get the list of dimms with serial-id info
37782214Sav145390  * from the SP.
37792214Sav145390  */
37802214Sav145390 mc_dimm_info_t *
37812214Sav145390 mc_get_dimm_list(mc_opl_t *mcp)
37822214Sav145390 {
37832214Sav145390 	uint32_t	bufsz;
37842214Sav145390 	uint32_t	maxbufsz;
37852214Sav145390 	int		ret;
37862214Sav145390 	int		sexp;
37872214Sav145390 	board_dimm_info_t *bd_dimmp;
37882214Sav145390 	mc_dimm_info_t	*dimm_list = NULL;
37892214Sav145390 
37902214Sav145390 	maxbufsz = bufsz = sizeof (board_dimm_info_t) +
37912214Sav145390 	    ((MCOPL_MAX_DIMMNAME +  MCOPL_MAX_SERIAL +
37922214Sav145390 	    MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS);
37932214Sav145390 
37942214Sav145390 	bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP);
37952214Sav145390 	ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz);
37962214Sav145390 
37972214Sav145390 	MC_LOG("mc_get_dimm_list:  scf_service_getinfo returned=%d\n", ret);
37982214Sav145390 	if (ret == 0) {
37992214Sav145390 		sexp = sizeof (board_dimm_info_t) +
38002214Sav145390 		    ((bd_dimmp->bd_dnamesz +  bd_dimmp->bd_serialsz +
38012214Sav145390 		    bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms);
38022214Sav145390 
38032214Sav145390 		if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) &&
38042214Sav145390 		    (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) &&
38052214Sav145390 		    (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) &&
38062214Sav145390 		    (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) &&
38072214Sav145390 		    (sexp <= bufsz)) {
38082214Sav145390 
38092214Sav145390 #ifdef DEBUG
38102214Sav145390 			if (oplmc_debug)
38112214Sav145390 				mc_dump_dimm_info(bd_dimmp);
38122214Sav145390 #endif
38132214Sav145390 			dimm_list = mc_prepare_dimmlist(bd_dimmp);
38142214Sav145390 
38152214Sav145390 		} else {
38162214Sav145390 			cmn_err(CE_WARN, "DIMM info version mismatch\n");
38172214Sav145390 		}
38182214Sav145390 	}
38192214Sav145390 	kmem_free(bd_dimmp, maxbufsz);
38202214Sav145390 	MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list);
38212214Sav145390 	return (dimm_list);
38222214Sav145390 }
38232214Sav145390 
38242214Sav145390 /*
38253152Sav145390  * mc_prepare_dimmlist - Prepare the dimm list from the information
38263152Sav145390  * received from the SP.
38272214Sav145390  */
38282214Sav145390 mc_dimm_info_t *
38292214Sav145390 mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp)
38302214Sav145390 {
38312214Sav145390 	char	*dimm_name;
38322214Sav145390 	char	*serial;
38332214Sav145390 	char	*part;
38342214Sav145390 	int	dimm;
38352214Sav145390 	int	dnamesz = bd_dimmp->bd_dnamesz;
38362214Sav145390 	int	sersz = bd_dimmp->bd_serialsz;
38372214Sav145390 	int	partsz = bd_dimmp->bd_partnumsz;
38382214Sav145390 	mc_dimm_info_t	*dimm_list = NULL;
38392214Sav145390 	mc_dimm_info_t	*d;
38402214Sav145390 
38412214Sav145390 	dimm_name = (char *)(bd_dimmp + 1);
38422214Sav145390 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
38432214Sav145390 
38442214Sav145390 		d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t),
38452214Sav145390 		    KM_SLEEP);
38463373Sbm42561 
38473373Sbm42561 		bcopy(dimm_name, d->md_dimmname, dnamesz);
38483373Sbm42561 		d->md_dimmname[dnamesz] = 0;
38493373Sbm42561 
38502214Sav145390 		serial = dimm_name + dnamesz;
38513373Sbm42561 		bcopy(serial, d->md_serial, sersz);
38523373Sbm42561 		d->md_serial[sersz] = 0;
38533373Sbm42561 
38542214Sav145390 		part = serial + sersz;
38553373Sbm42561 		bcopy(part, d->md_partnum, partsz);
38563373Sbm42561 		d->md_partnum[partsz] = 0;
38572214Sav145390 
38582214Sav145390 		d->md_next = dimm_list;
38592214Sav145390 		dimm_list = d;
38602214Sav145390 		dimm_name = part + partsz;
38612214Sav145390 	}
38622214Sav145390 	return (dimm_list);
38632214Sav145390 }
38642214Sav145390 
3865*6693Swh31274 static int
3866*6693Swh31274 mc_get_mem_fmri(mc_flt_page_t *fpag, char **unum)
3867*6693Swh31274 {
3868*6693Swh31274 	if (fpag->fmri_addr == 0 || fpag->fmri_sz > MEM_FMRI_MAX_BUFSIZE)
3869*6693Swh31274 		return (EINVAL);
3870*6693Swh31274 
3871*6693Swh31274 	*unum = kmem_alloc(fpag->fmri_sz, KM_SLEEP);
3872*6693Swh31274 	if (copyin((void *)fpag->fmri_addr, *unum, fpag->fmri_sz) != 0) {
3873*6693Swh31274 		kmem_free(*unum, fpag->fmri_sz);
3874*6693Swh31274 		return (EFAULT);
3875*6693Swh31274 	}
3876*6693Swh31274 	return (0);
3877*6693Swh31274 }
3878*6693Swh31274 
3879*6693Swh31274 static int
3880*6693Swh31274 mc_scf_log_event(mc_flt_page_t *flt_pag)
3881*6693Swh31274 {
3882*6693Swh31274 	mc_opl_t *mcp;
3883*6693Swh31274 	int board, bank, slot;
3884*6693Swh31274 	int len, rv = 0;
3885*6693Swh31274 	char *unum, *sid;
3886*6693Swh31274 	char dname[MCOPL_MAX_DIMMNAME + 1];
3887*6693Swh31274 	size_t sid_sz;
3888*6693Swh31274 	uint64_t pa;
3889*6693Swh31274 	mc_flt_stat_t flt_stat;
3890*6693Swh31274 
3891*6693Swh31274 	if ((sid_sz = cpu_get_name_bufsize()) == 0)
3892*6693Swh31274 		return (ENOTSUP);
3893*6693Swh31274 
3894*6693Swh31274 	if ((rv = mc_get_mem_fmri(flt_pag, &unum)) != 0) {
3895*6693Swh31274 		MC_LOG("mc_scf_log_event: mc_get_mem_fmri failed\n");
3896*6693Swh31274 		return (rv);
3897*6693Swh31274 	}
3898*6693Swh31274 
3899*6693Swh31274 	sid = kmem_zalloc(sid_sz, KM_SLEEP);
3900*6693Swh31274 
3901*6693Swh31274 	if ((rv = mc_get_mem_sid(unum, sid, sid_sz, &len)) != 0) {
3902*6693Swh31274 		MC_LOG("mc_scf_log_event: mc_get_mem_sid failed\n");
3903*6693Swh31274 		goto out;
3904*6693Swh31274 	}
3905*6693Swh31274 
3906*6693Swh31274 	if ((rv = mc_get_mem_addr(unum, sid, (uint64_t)flt_pag->err_add,
3907*6693Swh31274 	    &pa)) != 0) {
3908*6693Swh31274 		MC_LOG("mc_scf_log_event: mc_get_mem_addr failed\n");
3909*6693Swh31274 		goto out;
3910*6693Swh31274 	}
3911*6693Swh31274 
3912*6693Swh31274 	if (parse_unum_memory(unum, &board, dname) != 0) {
3913*6693Swh31274 		MC_LOG("mc_scf_log_event: parse_unum_memory failed\n");
3914*6693Swh31274 		rv = EINVAL;
3915*6693Swh31274 		goto out;
3916*6693Swh31274 	}
3917*6693Swh31274 
3918*6693Swh31274 	if (board < 0) {
3919*6693Swh31274 		MC_LOG("mc_scf_log_event: Invalid board=%d dimm=%s\n",
3920*6693Swh31274 		    board, dname);
3921*6693Swh31274 		rv = EINVAL;
3922*6693Swh31274 		goto out;
3923*6693Swh31274 	}
3924*6693Swh31274 
3925*6693Swh31274 	if (dname_to_bankslot(dname, &bank, &slot) != 0) {
3926*6693Swh31274 		MC_LOG("mc_scf_log_event: dname_to_bankslot failed\n");
3927*6693Swh31274 		rv = EINVAL;
3928*6693Swh31274 		goto out;
3929*6693Swh31274 	}
3930*6693Swh31274 
3931*6693Swh31274 	mutex_enter(&mcmutex);
3932*6693Swh31274 
3933*6693Swh31274 	flt_stat.mf_err_add = flt_pag->err_add;
3934*6693Swh31274 	flt_stat.mf_err_log = flt_pag->err_log;
3935*6693Swh31274 	flt_stat.mf_flt_paddr = pa;
3936*6693Swh31274 
3937*6693Swh31274 	if ((mcp = mc_pa_to_mcp(pa)) == NULL) {
3938*6693Swh31274 		mutex_exit(&mcmutex);
3939*6693Swh31274 		MC_LOG("mc_scf_log_event: invalid pa\n");
3940*6693Swh31274 		rv = EINVAL;
3941*6693Swh31274 		goto out;
3942*6693Swh31274 	}
3943*6693Swh31274 
3944*6693Swh31274 	MC_LOG("mc_scf_log_event: DIMM%s, /LSB%d/B%d/%x, pa %lx elog %x\n",
3945*6693Swh31274 	    unum, mcp->mc_board_num, bank, flt_pag->err_add, pa,
3946*6693Swh31274 	    flt_pag->err_log);
3947*6693Swh31274 
3948*6693Swh31274 	mutex_enter(&mcp->mc_lock);
3949*6693Swh31274 
3950*6693Swh31274 	if (!pa_is_valid(mcp, pa)) {
3951*6693Swh31274 		mutex_exit(&mcp->mc_lock);
3952*6693Swh31274 		mutex_exit(&mcmutex);
3953*6693Swh31274 		rv = EINVAL;
3954*6693Swh31274 		goto out;
3955*6693Swh31274 	}
3956*6693Swh31274 
3957*6693Swh31274 	rv = 0;
3958*6693Swh31274 
3959*6693Swh31274 	mc_queue_scf_log(mcp, &flt_stat, bank);
3960*6693Swh31274 
3961*6693Swh31274 	mutex_exit(&mcp->mc_lock);
3962*6693Swh31274 	mutex_exit(&mcmutex);
3963*6693Swh31274 
3964*6693Swh31274 out:
3965*6693Swh31274 	kmem_free(unum, flt_pag->fmri_sz);
3966*6693Swh31274 	kmem_free(sid, sid_sz);
3967*6693Swh31274 
3968*6693Swh31274 	return (rv);
3969*6693Swh31274 }
3970*6693Swh31274 
39712214Sav145390 #ifdef DEBUG
39722214Sav145390 void
39732214Sav145390 mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz)
39742214Sav145390 {
39752214Sav145390 	char dname[MCOPL_MAX_DIMMNAME + 1];
39762214Sav145390 	char serial[MCOPL_MAX_SERIAL + 1];
39772214Sav145390 	char part[ MCOPL_MAX_PARTNUM + 1];
39782214Sav145390 	char *b;
39792214Sav145390 
39802214Sav145390 	b = buf;
39813373Sbm42561 	bcopy(b, dname, dnamesz);
39823373Sbm42561 	dname[dnamesz] = 0;
39833373Sbm42561 
39842214Sav145390 	b += dnamesz;
39853373Sbm42561 	bcopy(b, serial, serialsz);
39863373Sbm42561 	serial[serialsz] = 0;
39873373Sbm42561 
39882214Sav145390 	b += serialsz;
39893373Sbm42561 	bcopy(b, part, partnumsz);
39903373Sbm42561 	part[partnumsz] = 0;
39913373Sbm42561 
39922214Sav145390 	printf("DIMM=%s  Serial=%s PartNum=%s\n", dname, serial, part);
39932214Sav145390 }
39942214Sav145390 
39952214Sav145390 void
39962214Sav145390 mc_dump_dimm_info(board_dimm_info_t *bd_dimmp)
39972214Sav145390 {
39982214Sav145390 	int	dimm;
39992214Sav145390 	int	dnamesz = bd_dimmp->bd_dnamesz;
40002214Sav145390 	int	sersz = bd_dimmp->bd_serialsz;
40012214Sav145390 	int	partsz = bd_dimmp->bd_partnumsz;
40022214Sav145390 	char	*buf;
40032214Sav145390 
40042214Sav145390 	printf("Version=%d Board=%02d DIMMs=%d NameSize=%d "
40052214Sav145390 	    "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version,
40062214Sav145390 	    bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz,
40072214Sav145390 	    bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz);
40082214Sav145390 	printf("======================================================\n");
40092214Sav145390 
40102214Sav145390 	buf = (char *)(bd_dimmp + 1);
40112214Sav145390 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
40122214Sav145390 		mc_dump_dimm(buf, dnamesz, sersz, partsz);
40132214Sav145390 		buf += dnamesz + sersz + partsz;
40142214Sav145390 	}
40152214Sav145390 	printf("======================================================\n");
40162214Sav145390 }
40172214Sav145390 
40182214Sav145390 
40192214Sav145390 /* ARGSUSED */
40202214Sav145390 static int
40212214Sav145390 mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
40222214Sav145390 	int *rvalp)
40232214Sav145390 {
4024*6693Swh31274 	caddr_t	buf, kbuf;
40252214Sav145390 	uint64_t pa;
40262214Sav145390 	int rv = 0;
40272214Sav145390 	int i;
40282214Sav145390 	uint32_t flags;
40292214Sav145390 	static uint32_t offset = 0;
40302214Sav145390 
40312214Sav145390 
40322214Sav145390 	flags = (cmd >> 4) & 0xfffffff;
40332214Sav145390 
40342214Sav145390 	cmd &= 0xf;
40352214Sav145390 
40362214Sav145390 	MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags);
40372214Sav145390 
40382214Sav145390 	if (arg != NULL) {
40392214Sav145390 		if (ddi_copyin((const void *)arg, (void *)&pa,
40405080Swh31274 		    sizeof (uint64_t), 0) < 0) {
40412214Sav145390 			rv = EFAULT;
40422214Sav145390 			return (rv);
40432214Sav145390 		}
40442214Sav145390 		buf = NULL;
40452214Sav145390 	} else {
40462214Sav145390 		buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP);
40472214Sav145390 
40482214Sav145390 		pa = va_to_pa(buf);
40492214Sav145390 		pa += offset;
40502214Sav145390 
40512214Sav145390 		offset += 64;
40522214Sav145390 		if (offset >= PAGESIZE)
40532214Sav145390 			offset = 0;
40542214Sav145390 	}
40552214Sav145390 
40562214Sav145390 	switch (cmd) {
40572214Sav145390 	case MCI_CE:
40585080Swh31274 		mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa, flags);
40592214Sav145390 		break;
40602214Sav145390 	case MCI_PERM_CE:
40615080Swh31274 		mc_inject_error(MC_INJECT_PERMANENT_CE, pa, flags);
40622214Sav145390 		break;
40632214Sav145390 	case MCI_UE:
40645080Swh31274 		mc_inject_error(MC_INJECT_UE, pa, flags);
40652214Sav145390 		break;
40662214Sav145390 	case MCI_M_CE:
40675080Swh31274 		mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa, flags);
40682214Sav145390 		break;
40692214Sav145390 	case MCI_M_PCE:
40705080Swh31274 		mc_inject_error(MC_INJECT_PERMANENT_MCE, pa, flags);
40712214Sav145390 		break;
40722214Sav145390 	case MCI_M_UE:
40735080Swh31274 		mc_inject_error(MC_INJECT_MUE, pa, flags);
40742214Sav145390 		break;
40752214Sav145390 	case MCI_CMP:
40765080Swh31274 		mc_inject_error(MC_INJECT_CMPE, pa, flags);
40772214Sav145390 		break;
40782214Sav145390 	case MCI_NOP:
40795080Swh31274 		mc_inject_error(MC_INJECT_NOP, pa, flags); break;
40802214Sav145390 	case MCI_SHOW_ALL:
40812214Sav145390 		mc_debug_show_all = 1;
40822214Sav145390 		break;
40832214Sav145390 	case MCI_SHOW_NONE:
40842214Sav145390 		mc_debug_show_all = 0;
40852214Sav145390 		break;
40862214Sav145390 	case MCI_ALLOC:
40872214Sav145390 		/*
40882214Sav145390 		 * just allocate some kernel memory and never free it
40892214Sav145390 		 * 512 MB seems to be the maximum size supported.
40902214Sav145390 		 */
40912214Sav145390 		cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512);
40922214Sav145390 		for (i = 0; i < flags; i++) {
4093*6693Swh31274 			kbuf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP);
40942214Sav145390 			cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n",
4095*6693Swh31274 			    (u_longlong_t)kbuf, (u_longlong_t)va_to_pa(kbuf));
40962214Sav145390 		}
40972214Sav145390 		break;
40982214Sav145390 	case MCI_SUSPEND:
40992214Sav145390 		(void) opl_mc_suspend();
41002214Sav145390 		break;
41012214Sav145390 	case MCI_RESUME:
41022214Sav145390 		(void) opl_mc_resume();
41032214Sav145390 		break;
41042214Sav145390 	default:
41052214Sav145390 		rv = ENXIO;
41062214Sav145390 	}
4107*6693Swh31274 	if (buf)
4108*6693Swh31274 		kmem_free(buf, PAGESIZE);
4109*6693Swh31274 
41102214Sav145390 	return (rv);
41112214Sav145390 }
41122214Sav145390 
41132214Sav145390 #endif /* DEBUG */
4114