xref: /illumos-gate/usr/src/uts/sun4u/opl/io/mc-opl.c (revision d8a0cca9)
125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
211e2e7a75Shuah /*
2268ac2337Sjl  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
231e2e7a75Shuah  * Use is subject to license terms.
241e2e7a75Shuah  */
2525cf1a30Sjl /*
2625cf1a30Sjl  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
2725cf1a30Sjl  */
2825cf1a30Sjl 
2925cf1a30Sjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
3025cf1a30Sjl 
3125cf1a30Sjl #include <sys/types.h>
3225cf1a30Sjl #include <sys/sysmacros.h>
3325cf1a30Sjl #include <sys/conf.h>
3425cf1a30Sjl #include <sys/modctl.h>
3525cf1a30Sjl #include <sys/stat.h>
3625cf1a30Sjl #include <sys/async.h>
371e2e7a75Shuah #include <sys/machcpuvar.h>
3825cf1a30Sjl #include <sys/machsystm.h>
390cc8ae86Sav #include <sys/promif.h>
4025cf1a30Sjl #include <sys/ksynch.h>
4125cf1a30Sjl #include <sys/ddi.h>
4225cf1a30Sjl #include <sys/sunddi.h>
43*d8a0cca9Swh #include <sys/sunndi.h>
4425cf1a30Sjl #include <sys/ddifm.h>
4525cf1a30Sjl #include <sys/fm/protocol.h>
4625cf1a30Sjl #include <sys/fm/util.h>
4725cf1a30Sjl #include <sys/kmem.h>
4825cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h>
4925cf1a30Sjl #include <sys/memlist.h>
5025cf1a30Sjl #include <sys/param.h>
510cc8ae86Sav #include <sys/disp.h>
5225cf1a30Sjl #include <vm/page.h>
5325cf1a30Sjl #include <sys/mc-opl.h>
540cc8ae86Sav #include <sys/opl.h>
550cc8ae86Sav #include <sys/opl_dimm.h>
560cc8ae86Sav #include <sys/scfd/scfostoescf.h>
57cfb9e062Shyw #include <sys/cpu_module.h>
58cfb9e062Shyw #include <vm/seg_kmem.h>
59cfb9e062Shyw #include <sys/vmem.h>
60cfb9e062Shyw #include <vm/hat_sfmmu.h>
61cfb9e062Shyw #include <sys/vmsystm.h>
62738dd194Shyw #include <sys/membar.h>
6325cf1a30Sjl 
6425cf1a30Sjl /*
6525cf1a30Sjl  * Function prototypes
6625cf1a30Sjl  */
6725cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *);
6825cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *);
6925cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
7025cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
7125cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
7225cf1a30Sjl 
730cc8ae86Sav static int mc_poll_init(void);
740cc8ae86Sav static void mc_poll_fini(void);
7525cf1a30Sjl static int mc_board_add(mc_opl_t *mcp);
7625cf1a30Sjl static int mc_board_del(mc_opl_t *mcp);
7725cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag);
7825cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag);
790cc8ae86Sav int opl_mc_suspend(void);
800cc8ae86Sav int opl_mc_resume(void);
8125cf1a30Sjl 
8225cf1a30Sjl static void insert_mcp(mc_opl_t *mcp);
8325cf1a30Sjl static void delete_mcp(mc_opl_t *mcp);
8425cf1a30Sjl 
8525cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr);
8625cf1a30Sjl 
87738dd194Shyw static int mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa);
8825cf1a30Sjl 
8925cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *);
900cc8ae86Sav int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr);
910cc8ae86Sav int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
920cc8ae86Sav int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp);
930cc8ae86Sav int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
940cc8ae86Sav     int buflen, int *lenp);
950cc8ae86Sav mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp);
960cc8ae86Sav mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp);
970cc8ae86Sav int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank,
980cc8ae86Sav     uint32_t mf_type, uint32_t d_slot);
990cc8ae86Sav static void mc_free_dimm_list(mc_dimm_info_t *d);
10025cf1a30Sjl static void mc_get_mlist(mc_opl_t *);
1010cc8ae86Sav static void mc_polling(void);
1020cc8ae86Sav static int mc_opl_get_physical_board(int);
1030cc8ae86Sav 
1040cc8ae86Sav #ifdef	DEBUG
1050cc8ae86Sav static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *);
1060cc8ae86Sav void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz);
1070cc8ae86Sav void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp);
1080cc8ae86Sav #endif
10925cf1a30Sjl 
11025cf1a30Sjl #pragma weak opl_get_physical_board
11125cf1a30Sjl extern int opl_get_physical_board(int);
1120cc8ae86Sav extern int plat_max_boards(void);
11325cf1a30Sjl 
11425cf1a30Sjl /*
11525cf1a30Sjl  * Configuration data structures
11625cf1a30Sjl  */
11725cf1a30Sjl static struct cb_ops mc_cb_ops = {
11825cf1a30Sjl 	mc_open,			/* open */
11925cf1a30Sjl 	mc_close,			/* close */
12025cf1a30Sjl 	nulldev,			/* strategy */
12125cf1a30Sjl 	nulldev,			/* print */
12225cf1a30Sjl 	nodev,				/* dump */
12325cf1a30Sjl 	nulldev,			/* read */
12425cf1a30Sjl 	nulldev,			/* write */
12525cf1a30Sjl 	mc_ioctl,			/* ioctl */
12625cf1a30Sjl 	nodev,				/* devmap */
12725cf1a30Sjl 	nodev,				/* mmap */
12825cf1a30Sjl 	nodev,				/* segmap */
12925cf1a30Sjl 	nochpoll,			/* poll */
13025cf1a30Sjl 	ddi_prop_op,			/* cb_prop_op */
13125cf1a30Sjl 	0,				/* streamtab */
13225cf1a30Sjl 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
13325cf1a30Sjl 	CB_REV,				/* rev */
13425cf1a30Sjl 	nodev,				/* cb_aread */
13525cf1a30Sjl 	nodev				/* cb_awrite */
13625cf1a30Sjl };
13725cf1a30Sjl 
13825cf1a30Sjl static struct dev_ops mc_ops = {
13925cf1a30Sjl 	DEVO_REV,			/* rev */
14025cf1a30Sjl 	0,				/* refcnt  */
14125cf1a30Sjl 	ddi_getinfo_1to1,		/* getinfo */
14225cf1a30Sjl 	nulldev,			/* identify */
14325cf1a30Sjl 	nulldev,			/* probe */
14425cf1a30Sjl 	mc_attach,			/* attach */
14525cf1a30Sjl 	mc_detach,			/* detach */
14625cf1a30Sjl 	nulldev,			/* reset */
14725cf1a30Sjl 	&mc_cb_ops,			/* cb_ops */
14825cf1a30Sjl 	(struct bus_ops *)0,		/* bus_ops */
14925cf1a30Sjl 	nulldev				/* power */
15025cf1a30Sjl };
15125cf1a30Sjl 
15225cf1a30Sjl /*
15325cf1a30Sjl  * Driver globals
15425cf1a30Sjl  */
15525cf1a30Sjl 
1560cc8ae86Sav static enum {
1570cc8ae86Sav 	MODEL_FF1 = 0,
1580cc8ae86Sav 	MODEL_FF2 = 1,
1590cc8ae86Sav 	MODEL_DC = 2
1600cc8ae86Sav } plat_model = MODEL_DC;	/* The default behaviour is DC */
1610cc8ae86Sav 
1620cc8ae86Sav static struct plat_model_names {
1630cc8ae86Sav 	const char *unit_name;
1640cc8ae86Sav 	const char *mem_name;
1650cc8ae86Sav } model_names[] = {
1660cc8ae86Sav 	{ "MBU_A", "MEMB" },
1670cc8ae86Sav 	{ "MBU_B", "MEMB" },
1680cc8ae86Sav 	{ "CMU", "" }
1690cc8ae86Sav };
17025cf1a30Sjl 
1710cc8ae86Sav /*
1720cc8ae86Sav  * The DIMM Names for DC platform.
1730cc8ae86Sav  * The index into this table is made up of (bank, dslot),
1740cc8ae86Sav  * Where dslot occupies bits 0-1 and bank occupies 2-4.
1750cc8ae86Sav  */
1760cc8ae86Sav static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = {
1770cc8ae86Sav 	/* --------CMUnn----------- */
1780cc8ae86Sav 	/* --CS0-----|--CS1------ */
1790cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
180c964b0e6Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
181c964b0e6Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */
182c964b0e6Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */
183c964b0e6Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */
184c964b0e6Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */
185c964b0e6Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */
186c964b0e6Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */
187c964b0e6Sraghuram 	"31A", "30A", "31B", "30B"  /* Bank 7 (MAC 3 bank 1) */
1880cc8ae86Sav };
1890cc8ae86Sav 
1900cc8ae86Sav /*
1910cc8ae86Sav  * The DIMM Names for FF1/FF2 platforms.
1920cc8ae86Sav  * The index into this table is made up of (board, bank, dslot),
1930cc8ae86Sav  * Where dslot occupies bits 0-1, bank occupies 2-4 and
1940cc8ae86Sav  * board occupies the bit 5.
1950cc8ae86Sav  */
1960cc8ae86Sav static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = {
1970cc8ae86Sav 	/* --------CMU0---------- */
1980cc8ae86Sav 	/* --CS0-----|--CS1------ */
1990cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
200c964b0e6Sraghuram 	"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
201c964b0e6Sraghuram 	"01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */
202c964b0e6Sraghuram 	"13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */
203c964b0e6Sraghuram 	"11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */
204c964b0e6Sraghuram 	"23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */
205c964b0e6Sraghuram 	"21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */
206c964b0e6Sraghuram 	"33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */
207c964b0e6Sraghuram 	"31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */
2080cc8ae86Sav 	/* --------CMU1---------- */
2090cc8ae86Sav 	/* --CS0-----|--CS1------ */
2100cc8ae86Sav 	/* -H-|--L-- | -H- | -L-- */
211c964b0e6Sraghuram 	"43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */
212c964b0e6Sraghuram 	"41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */
213c964b0e6Sraghuram 	"53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */
214c964b0e6Sraghuram 	"51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */
215c964b0e6Sraghuram 	"63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */
216c964b0e6Sraghuram 	"61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */
217c964b0e6Sraghuram 	"73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */
218c964b0e6Sraghuram 	"71A", "70A", "71B", "70B"  /* Bank 7 (MAC 3 bank 1) */
2190cc8ae86Sav };
2200cc8ae86Sav 
2210cc8ae86Sav #define	BD_BK_SLOT_TO_INDEX(bd, bk, s)			\
2220cc8ae86Sav 	(((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03))
2230cc8ae86Sav 
2240cc8ae86Sav #define	INDEX_TO_BANK(i)			(((i) & 0x1C) >> 2)
2250cc8ae86Sav #define	INDEX_TO_SLOT(i)			((i) & 0x03)
2260cc8ae86Sav 
227aeb241b2Sav #define	SLOT_TO_CS(slot)	((slot & 0x3) >> 1)
228aeb241b2Sav 
2290cc8ae86Sav /* Isolation unit size is 64 MB */
2300cc8ae86Sav #define	MC_ISOLATION_BSIZE	(64 * 1024 * 1024)
2310cc8ae86Sav 
2320cc8ae86Sav #define	MC_MAX_SPEEDS 7
2330cc8ae86Sav 
2340cc8ae86Sav typedef struct {
2350cc8ae86Sav 	uint32_t mc_speeds;
2360cc8ae86Sav 	uint32_t mc_period;
2370cc8ae86Sav } mc_scan_speed_t;
2380cc8ae86Sav 
2390cc8ae86Sav #define	MC_CNTL_SPEED_SHIFT 26
2400cc8ae86Sav 
24137afe445Shyw /*
24237afe445Shyw  * In mirror mode, we normalized the bank idx to "even" since
24337afe445Shyw  * the HW treats them as one unit w.r.t programming.
24437afe445Shyw  * This bank index will be the "effective" bank index.
24537afe445Shyw  * All mirrored bank state info on mc_period, mc_speedup_period
24637afe445Shyw  * will be stored in the even bank structure to avoid code duplication.
24737afe445Shyw  */
24837afe445Shyw #define	MIRROR_IDX(bankidx)	(bankidx & ~1)
24937afe445Shyw 
2500cc8ae86Sav static mc_scan_speed_t	mc_scan_speeds[MC_MAX_SPEEDS] = {
2510cc8ae86Sav 	{0x6 << MC_CNTL_SPEED_SHIFT, 0},
2520cc8ae86Sav 	{0x5 << MC_CNTL_SPEED_SHIFT, 32},
2530cc8ae86Sav 	{0x4 << MC_CNTL_SPEED_SHIFT, 64},
2540cc8ae86Sav 	{0x3 << MC_CNTL_SPEED_SHIFT, 128},
2550cc8ae86Sav 	{0x2 << MC_CNTL_SPEED_SHIFT, 256},
2560cc8ae86Sav 	{0x1 << MC_CNTL_SPEED_SHIFT, 512},
2570cc8ae86Sav 	{0x0 << MC_CNTL_SPEED_SHIFT, 1024}
2580cc8ae86Sav };
2590cc8ae86Sav 
2600cc8ae86Sav static uint32_t	mc_max_speed = (0x6 << 26);
2610cc8ae86Sav 
2620cc8ae86Sav int mc_isolation_bsize = MC_ISOLATION_BSIZE;
2630cc8ae86Sav int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC;
2640cc8ae86Sav int mc_max_scf_retry = 16;
2650cc8ae86Sav int mc_max_scf_logs = 64;
2660cc8ae86Sav int mc_max_errlog_processed = BANKNUM_PER_SB*2;
2670cc8ae86Sav int mc_scan_period = 12 * 60 * 60;	/* 12 hours period */
2680cc8ae86Sav int mc_max_rewrite_loop = 100;
2690cc8ae86Sav int mc_rewrite_delay = 10;
2700cc8ae86Sav /*
2710cc8ae86Sav  * it takes SCF about 300 m.s. to process a requst.  We can bail out
2720cc8ae86Sav  * if it is busy.  It does not pay to wait for it too long.
2730cc8ae86Sav  */
2740cc8ae86Sav int mc_max_scf_loop = 2;
2750cc8ae86Sav int mc_scf_delay = 100;
2760cc8ae86Sav int mc_pce_dropped = 0;
2770cc8ae86Sav int mc_poll_priority = MINCLSYSPRI;
27825cf1a30Sjl 
2790cc8ae86Sav 
2800cc8ae86Sav /*
2811039f409Sav  * Mutex hierarchy in mc-opl
2820cc8ae86Sav  * If both mcmutex and mc_lock must be held,
2830cc8ae86Sav  * mcmutex must be acquired first, and then mc_lock.
2840cc8ae86Sav  */
2850cc8ae86Sav 
2860cc8ae86Sav static kmutex_t mcmutex;
2870cc8ae86Sav mc_opl_t *mc_instances[OPL_MAX_BOARDS];
2880cc8ae86Sav 
2890cc8ae86Sav static kmutex_t mc_polling_lock;
2900cc8ae86Sav static kcondvar_t mc_polling_cv;
2910cc8ae86Sav static kcondvar_t mc_poll_exit_cv;
2920cc8ae86Sav static int mc_poll_cmd = 0;
2930cc8ae86Sav static int mc_pollthr_running = 0;
2940cc8ae86Sav int mc_timeout_period = 0; /* this is in m.s. */
29525cf1a30Sjl void *mc_statep;
29625cf1a30Sjl 
29725cf1a30Sjl #ifdef	DEBUG
2982742aa22Shyw int oplmc_debug = 0;
29925cf1a30Sjl #endif
30025cf1a30Sjl 
3010cc8ae86Sav static int mc_debug_show_all = 0;
30225cf1a30Sjl 
30325cf1a30Sjl extern struct mod_ops mod_driverops;
30425cf1a30Sjl 
30525cf1a30Sjl static struct modldrv modldrv = {
30625cf1a30Sjl 	&mod_driverops,			/* module type, this one is a driver */
3070cc8ae86Sav 	"OPL Memory-controller %I%",	/* module name */
30825cf1a30Sjl 	&mc_ops,			/* driver ops */
30925cf1a30Sjl };
31025cf1a30Sjl 
31125cf1a30Sjl static struct modlinkage modlinkage = {
31225cf1a30Sjl 	MODREV_1,		/* rev */
31325cf1a30Sjl 	(void *)&modldrv,
31425cf1a30Sjl 	NULL
31525cf1a30Sjl };
31625cf1a30Sjl 
31725cf1a30Sjl #pragma weak opl_get_mem_unum
3180cc8ae86Sav #pragma weak opl_get_mem_sid
3190cc8ae86Sav #pragma weak opl_get_mem_offset
3200cc8ae86Sav #pragma weak opl_get_mem_addr
3210cc8ae86Sav 
32225cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *);
3230cc8ae86Sav extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp);
3240cc8ae86Sav extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp);
3250cc8ae86Sav extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset,
3260cc8ae86Sav     uint64_t *paddr);
3270cc8ae86Sav 
32825cf1a30Sjl 
32925cf1a30Sjl /*
33025cf1a30Sjl  * pseudo-mc node portid format
33125cf1a30Sjl  *
33225cf1a30Sjl  *		[10]   = 0
33325cf1a30Sjl  *		[9]    = 1
33425cf1a30Sjl  *		[8]    = LSB_ID[4] = 0
33525cf1a30Sjl  *		[7:4]  = LSB_ID[3:0]
33625cf1a30Sjl  *		[3:0]  = 0
33725cf1a30Sjl  *
33825cf1a30Sjl  */
33925cf1a30Sjl 
34025cf1a30Sjl /*
34125cf1a30Sjl  * These are the module initialization routines.
34225cf1a30Sjl  */
34325cf1a30Sjl int
34425cf1a30Sjl _init(void)
34525cf1a30Sjl {
3460cc8ae86Sav 	int	error;
3470cc8ae86Sav 	int	plen;
3480cc8ae86Sav 	char	model[20];
3490cc8ae86Sav 	pnode_t	node;
35025cf1a30Sjl 
35125cf1a30Sjl 
35225cf1a30Sjl 	if ((error = ddi_soft_state_init(&mc_statep,
35325cf1a30Sjl 	    sizeof (mc_opl_t), 1)) != 0)
35425cf1a30Sjl 		return (error);
35525cf1a30Sjl 
3560cc8ae86Sav 	if ((error = mc_poll_init()) != 0) {
3570cc8ae86Sav 		ddi_soft_state_fini(&mc_statep);
3580cc8ae86Sav 		return (error);
3590cc8ae86Sav 	}
3600cc8ae86Sav 
36125cf1a30Sjl 	mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
36225cf1a30Sjl 	if (&opl_get_mem_unum)
36325cf1a30Sjl 		opl_get_mem_unum = mc_get_mem_unum;
3640cc8ae86Sav 	if (&opl_get_mem_sid)
3650cc8ae86Sav 		opl_get_mem_sid = mc_get_mem_sid;
3660cc8ae86Sav 	if (&opl_get_mem_offset)
3670cc8ae86Sav 		opl_get_mem_offset = mc_get_mem_offset;
3680cc8ae86Sav 	if (&opl_get_mem_addr)
3690cc8ae86Sav 		opl_get_mem_addr = mc_get_mem_addr;
3700cc8ae86Sav 
3710cc8ae86Sav 	node = prom_rootnode();
3720cc8ae86Sav 	plen = prom_getproplen(node, "model");
3730cc8ae86Sav 
3740cc8ae86Sav 	if (plen > 0 && plen < sizeof (model)) {
3750cc8ae86Sav 		(void) prom_getprop(node, "model", model);
3760cc8ae86Sav 		model[plen] = '\0';
3770cc8ae86Sav 		if (strcmp(model, "FF1") == 0)
3780cc8ae86Sav 			plat_model = MODEL_FF1;
3790cc8ae86Sav 		else if (strcmp(model, "FF2") == 0)
3800cc8ae86Sav 			plat_model = MODEL_FF2;
3810cc8ae86Sav 		else if (strncmp(model, "DC", 2) == 0)
3820cc8ae86Sav 			plat_model = MODEL_DC;
3830cc8ae86Sav 	}
38425cf1a30Sjl 
38525cf1a30Sjl 	error =  mod_install(&modlinkage);
38625cf1a30Sjl 	if (error != 0) {
38725cf1a30Sjl 		if (&opl_get_mem_unum)
38825cf1a30Sjl 			opl_get_mem_unum = NULL;
3890cc8ae86Sav 		if (&opl_get_mem_sid)
3900cc8ae86Sav 			opl_get_mem_sid = NULL;
3910cc8ae86Sav 		if (&opl_get_mem_offset)
3920cc8ae86Sav 			opl_get_mem_offset = NULL;
3930cc8ae86Sav 		if (&opl_get_mem_addr)
3940cc8ae86Sav 			opl_get_mem_addr = NULL;
39525cf1a30Sjl 		mutex_destroy(&mcmutex);
3960cc8ae86Sav 		mc_poll_fini();
39725cf1a30Sjl 		ddi_soft_state_fini(&mc_statep);
39825cf1a30Sjl 	}
39925cf1a30Sjl 	return (error);
40025cf1a30Sjl }
40125cf1a30Sjl 
40225cf1a30Sjl int
40325cf1a30Sjl _fini(void)
40425cf1a30Sjl {
40525cf1a30Sjl 	int error;
40625cf1a30Sjl 
40725cf1a30Sjl 	if ((error = mod_remove(&modlinkage)) != 0)
40825cf1a30Sjl 		return (error);
40925cf1a30Sjl 
41025cf1a30Sjl 	if (&opl_get_mem_unum)
41125cf1a30Sjl 		opl_get_mem_unum = NULL;
4120cc8ae86Sav 	if (&opl_get_mem_sid)
4130cc8ae86Sav 		opl_get_mem_sid = NULL;
4140cc8ae86Sav 	if (&opl_get_mem_offset)
4150cc8ae86Sav 		opl_get_mem_offset = NULL;
4160cc8ae86Sav 	if (&opl_get_mem_addr)
4170cc8ae86Sav 		opl_get_mem_addr = NULL;
41825cf1a30Sjl 
4190cc8ae86Sav 	mutex_destroy(&mcmutex);
4200cc8ae86Sav 	mc_poll_fini();
42125cf1a30Sjl 	ddi_soft_state_fini(&mc_statep);
42225cf1a30Sjl 
42325cf1a30Sjl 	return (0);
42425cf1a30Sjl }
42525cf1a30Sjl 
42625cf1a30Sjl int
42725cf1a30Sjl _info(struct modinfo *modinfop)
42825cf1a30Sjl {
42925cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
43025cf1a30Sjl }
43125cf1a30Sjl 
4320cc8ae86Sav static void
4330cc8ae86Sav mc_polling_thread()
4340cc8ae86Sav {
4350cc8ae86Sav 	mutex_enter(&mc_polling_lock);
4360cc8ae86Sav 	mc_pollthr_running = 1;
4370cc8ae86Sav 	while (!(mc_poll_cmd & MC_POLL_EXIT)) {
4380cc8ae86Sav 		mc_polling();
4390cc8ae86Sav 		cv_timedwait(&mc_polling_cv, &mc_polling_lock,
4400cc8ae86Sav 		    ddi_get_lbolt() + mc_timeout_period);
4410cc8ae86Sav 	}
4420cc8ae86Sav 	mc_pollthr_running = 0;
4430cc8ae86Sav 
4440cc8ae86Sav 	/*
4450cc8ae86Sav 	 * signal if any one is waiting for this thread to exit.
4460cc8ae86Sav 	 */
4470cc8ae86Sav 	cv_signal(&mc_poll_exit_cv);
4480cc8ae86Sav 	mutex_exit(&mc_polling_lock);
4490cc8ae86Sav 	thread_exit();
4500cc8ae86Sav 	/* NOTREACHED */
4510cc8ae86Sav }
4520cc8ae86Sav 
4530cc8ae86Sav static int
4540cc8ae86Sav mc_poll_init()
4550cc8ae86Sav {
4560cc8ae86Sav 	mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL);
4570cc8ae86Sav 	cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL);
4580cc8ae86Sav 	cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL);
4590cc8ae86Sav 	return (0);
4600cc8ae86Sav }
4610cc8ae86Sav 
4620cc8ae86Sav static void
4630cc8ae86Sav mc_poll_fini()
4640cc8ae86Sav {
4650cc8ae86Sav 	mutex_enter(&mc_polling_lock);
4660cc8ae86Sav 	if (mc_pollthr_running) {
4670cc8ae86Sav 		mc_poll_cmd = MC_POLL_EXIT;
4680cc8ae86Sav 		cv_signal(&mc_polling_cv);
4690cc8ae86Sav 		while (mc_pollthr_running) {
4700cc8ae86Sav 			cv_wait(&mc_poll_exit_cv, &mc_polling_lock);
4710cc8ae86Sav 		}
4720cc8ae86Sav 	}
4730cc8ae86Sav 	mutex_exit(&mc_polling_lock);
4740cc8ae86Sav 	mutex_destroy(&mc_polling_lock);
4750cc8ae86Sav 	cv_destroy(&mc_polling_cv);
4760cc8ae86Sav 	cv_destroy(&mc_poll_exit_cv);
4770cc8ae86Sav }
4780cc8ae86Sav 
47925cf1a30Sjl static int
48025cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
48125cf1a30Sjl {
48225cf1a30Sjl 	mc_opl_t *mcp;
48325cf1a30Sjl 	int instance;
4840cc8ae86Sav 	int rv;
48525cf1a30Sjl 
48625cf1a30Sjl 	/* get the instance of this devi */
48725cf1a30Sjl 	instance = ddi_get_instance(devi);
48825cf1a30Sjl 
48925cf1a30Sjl 	switch (cmd) {
49025cf1a30Sjl 	case DDI_ATTACH:
49125cf1a30Sjl 		break;
49225cf1a30Sjl 	case DDI_RESUME:
49325cf1a30Sjl 		mcp = ddi_get_soft_state(mc_statep, instance);
4940cc8ae86Sav 		rv = mc_resume(mcp, MC_DRIVER_SUSPENDED);
4950cc8ae86Sav 		return (rv);
49625cf1a30Sjl 	default:
49725cf1a30Sjl 		return (DDI_FAILURE);
49825cf1a30Sjl 	}
49925cf1a30Sjl 
50025cf1a30Sjl 	if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS)
50125cf1a30Sjl 		return (DDI_FAILURE);
50225cf1a30Sjl 
50325cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
50425cf1a30Sjl 		goto bad;
50525cf1a30Sjl 	}
50625cf1a30Sjl 
5070cc8ae86Sav 	if (mc_timeout_period == 0) {
5080cc8ae86Sav 		mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi,
509*d8a0cca9Swh 		    DDI_PROP_DONTPASS, "mc-timeout-interval-sec",
510*d8a0cca9Swh 		    mc_patrol_interval_sec);
511*d8a0cca9Swh 		mc_timeout_period = drv_usectohz(1000000 *
512*d8a0cca9Swh 		    mc_patrol_interval_sec / OPL_MAX_BOARDS);
5130cc8ae86Sav 	}
5140cc8ae86Sav 
51525cf1a30Sjl 	/* set informations in mc state */
51625cf1a30Sjl 	mcp->mc_dip = devi;
51725cf1a30Sjl 
51825cf1a30Sjl 	if (mc_board_add(mcp))
51925cf1a30Sjl 		goto bad;
52025cf1a30Sjl 
52125cf1a30Sjl 	insert_mcp(mcp);
5220cc8ae86Sav 
5230cc8ae86Sav 	/*
5240cc8ae86Sav 	 * Start the polling thread if it is not running already.
5250cc8ae86Sav 	 */
5260cc8ae86Sav 	mutex_enter(&mc_polling_lock);
5270cc8ae86Sav 	if (!mc_pollthr_running) {
5280cc8ae86Sav 		(void) thread_create(NULL, 0, (void (*)())mc_polling_thread,
529*d8a0cca9Swh 		    NULL, 0, &p0, TS_RUN, mc_poll_priority);
5300cc8ae86Sav 	}
5310cc8ae86Sav 	mutex_exit(&mc_polling_lock);
53225cf1a30Sjl 	ddi_report_dev(devi);
53325cf1a30Sjl 
53425cf1a30Sjl 	return (DDI_SUCCESS);
53525cf1a30Sjl 
53625cf1a30Sjl bad:
53725cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
53825cf1a30Sjl 	return (DDI_FAILURE);
53925cf1a30Sjl }
54025cf1a30Sjl 
54125cf1a30Sjl /* ARGSUSED */
54225cf1a30Sjl static int
54325cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
54425cf1a30Sjl {
5450cc8ae86Sav 	int rv;
54625cf1a30Sjl 	int instance;
54725cf1a30Sjl 	mc_opl_t *mcp;
54825cf1a30Sjl 
54925cf1a30Sjl 	/* get the instance of this devi */
55025cf1a30Sjl 	instance = ddi_get_instance(devi);
55125cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
55225cf1a30Sjl 		return (DDI_FAILURE);
55325cf1a30Sjl 	}
55425cf1a30Sjl 
55525cf1a30Sjl 	switch (cmd) {
55625cf1a30Sjl 	case DDI_SUSPEND:
5570cc8ae86Sav 		rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED);
5580cc8ae86Sav 		return (rv);
55925cf1a30Sjl 	case DDI_DETACH:
56025cf1a30Sjl 		break;
56125cf1a30Sjl 	default:
56225cf1a30Sjl 		return (DDI_FAILURE);
56325cf1a30Sjl 	}
56425cf1a30Sjl 
5650cc8ae86Sav 	delete_mcp(mcp);
56625cf1a30Sjl 	if (mc_board_del(mcp) != DDI_SUCCESS) {
56725cf1a30Sjl 		return (DDI_FAILURE);
56825cf1a30Sjl 	}
56925cf1a30Sjl 
57025cf1a30Sjl 	/* free up the soft state */
57125cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
57225cf1a30Sjl 
57325cf1a30Sjl 	return (DDI_SUCCESS);
57425cf1a30Sjl }
57525cf1a30Sjl 
57625cf1a30Sjl /* ARGSUSED */
57725cf1a30Sjl static int
57825cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
57925cf1a30Sjl {
58025cf1a30Sjl 	return (0);
58125cf1a30Sjl }
58225cf1a30Sjl 
58325cf1a30Sjl /* ARGSUSED */
58425cf1a30Sjl static int
58525cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
58625cf1a30Sjl {
58725cf1a30Sjl 	return (0);
58825cf1a30Sjl }
58925cf1a30Sjl 
59025cf1a30Sjl /* ARGSUSED */
59125cf1a30Sjl static int
59225cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
59325cf1a30Sjl 	int *rvalp)
59425cf1a30Sjl {
5950cc8ae86Sav #ifdef DEBUG
5960cc8ae86Sav 	return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp));
5970cc8ae86Sav #else
59825cf1a30Sjl 	return (ENXIO);
5990cc8ae86Sav #endif
60025cf1a30Sjl }
60125cf1a30Sjl 
60225cf1a30Sjl /*
60325cf1a30Sjl  * PA validity check:
604738dd194Shyw  * This function return 1 if the PA is a valid PA
605738dd194Shyw  * in the running Solaris instance i.e. in physinstall
606738dd194Shyw  * Otherwise, return 0.
60725cf1a30Sjl  */
60825cf1a30Sjl 
60925cf1a30Sjl /* ARGSUSED */
61025cf1a30Sjl static int
61125cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr)
61225cf1a30Sjl {
61325cf1a30Sjl 	if (mcp->mlist == NULL)
61425cf1a30Sjl 		mc_get_mlist(mcp);
61525cf1a30Sjl 
61625cf1a30Sjl 	if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) {
61725cf1a30Sjl 		return (1);
61825cf1a30Sjl 	}
61925cf1a30Sjl 	return (0);
62025cf1a30Sjl }
62125cf1a30Sjl 
62225cf1a30Sjl /*
62325cf1a30Sjl  * mac-pa translation routines.
62425cf1a30Sjl  *
62525cf1a30Sjl  *    Input: mc driver state, (LSB#, Bank#, DIMM address)
62625cf1a30Sjl  *    Output: physical address
62725cf1a30Sjl  *
62825cf1a30Sjl  *    Valid   - return value:  0
62925cf1a30Sjl  *    Invalid - return value: -1
63025cf1a30Sjl  */
63125cf1a30Sjl static int
63225cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa)
63325cf1a30Sjl {
63425cf1a30Sjl 	int i;
63525cf1a30Sjl 	uint64_t pa_offset = 0;
63625cf1a30Sjl 	int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1;
63725cf1a30Sjl 	int bank = maddr->ma_bank;
63825cf1a30Sjl 	mc_addr_t maddr1;
63925cf1a30Sjl 	int bank0, bank1;
64025cf1a30Sjl 
64125cf1a30Sjl 	MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
642*d8a0cca9Swh 	    maddr->ma_dimm_addr);
64325cf1a30Sjl 
64425cf1a30Sjl 	/* loc validity check */
64525cf1a30Sjl 	ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd);
64625cf1a30Sjl 	ASSERT(bank >= 0 && OPL_BANK_MAX > bank);
64725cf1a30Sjl 
64825cf1a30Sjl 	/* Do translation */
64925cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
65025cf1a30Sjl 		int pa_bit = 0;
65125cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
65225cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
65325cf1a30Sjl 			pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1;
65425cf1a30Sjl 		} else if (mc_bit == MP_NONE) {
65525cf1a30Sjl 			pa_bit = 0;
65625cf1a30Sjl 		} else if (mc_bit == MP_BANK_0) {
65725cf1a30Sjl 			pa_bit = bank & 1;
65825cf1a30Sjl 		} else if (mc_bit == MP_BANK_1) {
65925cf1a30Sjl 			pa_bit = (bank >> 1) & 1;
66025cf1a30Sjl 		} else if (mc_bit == MP_BANK_2) {
66125cf1a30Sjl 			pa_bit = (bank >> 2) & 1;
66225cf1a30Sjl 		}
66325cf1a30Sjl 		pa_offset |= ((uint64_t)pa_bit) << i;
66425cf1a30Sjl 	}
66525cf1a30Sjl 	*pa = mcp->mc_start_address + pa_offset;
66625cf1a30Sjl 	MC_LOG("pa = %lx\n", *pa);
66725cf1a30Sjl 
66825cf1a30Sjl 	if (pa_to_maddr(mcp, *pa, &maddr1) == -1) {
6690cc8ae86Sav 		cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to "
6700cc8ae86Sav 		    "convert PA %lx\n", maddr->ma_bd, bank,
6710cc8ae86Sav 		    maddr->ma_dimm_addr, *pa);
67225cf1a30Sjl 		return (-1);
67325cf1a30Sjl 	}
67425cf1a30Sjl 
6750cc8ae86Sav 	/*
6760cc8ae86Sav 	 * In mirror mode, PA is always translated to the even bank.
6770cc8ae86Sav 	 */
67825cf1a30Sjl 	if (IS_MIRROR(mcp, maddr->ma_bank)) {
67925cf1a30Sjl 		bank0 = maddr->ma_bank & ~(1);
68025cf1a30Sjl 		bank1 = maddr1.ma_bank & ~(1);
68125cf1a30Sjl 	} else {
68225cf1a30Sjl 		bank0 = maddr->ma_bank;
68325cf1a30Sjl 		bank1 = maddr1.ma_bank;
68425cf1a30Sjl 	}
68525cf1a30Sjl 	/*
68625cf1a30Sjl 	 * there is no need to check ma_bd because it is generated from
68725cf1a30Sjl 	 * mcp.  They are the same.
68825cf1a30Sjl 	 */
689*d8a0cca9Swh 	if ((bank0 == bank1) && (maddr->ma_dimm_addr ==
690*d8a0cca9Swh 	    maddr1.ma_dimm_addr)) {
69125cf1a30Sjl 		return (0);
69225cf1a30Sjl 	} else {
69325cf1a30Sjl 		cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, "
694*d8a0cca9Swh 		    "PA %lx, target /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
695*d8a0cca9Swh 		    maddr->ma_dimm_addr, *pa, maddr1.ma_bd, maddr1.ma_bank,
696*d8a0cca9Swh 		    maddr1.ma_dimm_addr);
69725cf1a30Sjl 		return (-1);
69825cf1a30Sjl 	}
69925cf1a30Sjl }
70025cf1a30Sjl 
70125cf1a30Sjl /*
70225cf1a30Sjl  * PA to CS (used by pa_to_maddr).
70325cf1a30Sjl  */
70425cf1a30Sjl static int
70525cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset)
70625cf1a30Sjl {
70725cf1a30Sjl 	int i;
708738dd194Shyw 	int cs = 1;
70925cf1a30Sjl 
71025cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
71125cf1a30Sjl 		/* MAC address bit<29> is arranged on the same PA bit */
71225cf1a30Sjl 		/* on both table. So we may use any table. */
71325cf1a30Sjl 		if (mcp->mc_trans_table[0][i] == CS_SHIFT) {
71425cf1a30Sjl 			cs = (pa_offset >> i) & 1;
71525cf1a30Sjl 			break;
71625cf1a30Sjl 		}
71725cf1a30Sjl 	}
71825cf1a30Sjl 	return (cs);
71925cf1a30Sjl }
72025cf1a30Sjl 
72125cf1a30Sjl /*
72225cf1a30Sjl  * PA to DIMM (used by pa_to_maddr).
72325cf1a30Sjl  */
72425cf1a30Sjl /* ARGSUSED */
72525cf1a30Sjl static uint32_t
72625cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset)
72725cf1a30Sjl {
72825cf1a30Sjl 	int i;
72925cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
73025cf1a30Sjl 	uint32_t dimm_addr = 0;
73125cf1a30Sjl 
73225cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
73325cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
73425cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
73525cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
73625cf1a30Sjl 			dimm_addr |= pa_bit_value << mc_bit;
73725cf1a30Sjl 		}
73825cf1a30Sjl 	}
739738dd194Shyw 	dimm_addr |= cs << CS_SHIFT;
74025cf1a30Sjl 	return (dimm_addr);
74125cf1a30Sjl }
74225cf1a30Sjl 
74325cf1a30Sjl /*
74425cf1a30Sjl  * PA to Bank (used by pa_to_maddr).
74525cf1a30Sjl  */
74625cf1a30Sjl static int
74725cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset)
74825cf1a30Sjl {
74925cf1a30Sjl 	int i;
75025cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
75125cf1a30Sjl 	int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT];
75225cf1a30Sjl 
75325cf1a30Sjl 
75425cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
75525cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
75625cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
75725cf1a30Sjl 		switch (mc_bit) {
75825cf1a30Sjl 		case MP_BANK_0:
75925cf1a30Sjl 			bankno |= pa_bit_value;
76025cf1a30Sjl 			break;
76125cf1a30Sjl 		case MP_BANK_1:
76225cf1a30Sjl 			bankno |= pa_bit_value << 1;
76325cf1a30Sjl 			break;
76425cf1a30Sjl 		case MP_BANK_2:
76525cf1a30Sjl 			bankno |= pa_bit_value << 2;
76625cf1a30Sjl 			break;
76725cf1a30Sjl 		}
76825cf1a30Sjl 	}
76925cf1a30Sjl 
77025cf1a30Sjl 	return (bankno);
77125cf1a30Sjl }
77225cf1a30Sjl 
77325cf1a30Sjl /*
77425cf1a30Sjl  * PA to MAC address translation
77525cf1a30Sjl  *
77625cf1a30Sjl  *   Input: MAC driver state, physicall adress
77725cf1a30Sjl  *   Output: LSB#, Bank id, mac address
77825cf1a30Sjl  *
77925cf1a30Sjl  *    Valid   - return value:  0
78025cf1a30Sjl  *    Invalid - return value: -1
78125cf1a30Sjl  */
78225cf1a30Sjl 
78325cf1a30Sjl int
78425cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr)
78525cf1a30Sjl {
78625cf1a30Sjl 	uint64_t pa_offset;
78725cf1a30Sjl 
788738dd194Shyw 	if (!mc_rangecheck_pa(mcp, pa))
78925cf1a30Sjl 		return (-1);
79025cf1a30Sjl 
79125cf1a30Sjl 	/* Do translation */
79225cf1a30Sjl 	pa_offset = pa - mcp->mc_start_address;
79325cf1a30Sjl 
79425cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
795aeb241b2Sav 	maddr->ma_phys_bd = mcp->mc_phys_board_num;
79625cf1a30Sjl 	maddr->ma_bank = pa_to_bank(mcp, pa_offset);
79725cf1a30Sjl 	maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset);
798*d8a0cca9Swh 	MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", pa_offset, maddr->ma_bd,
799*d8a0cca9Swh 	    maddr->ma_bank, maddr->ma_dimm_addr);
80025cf1a30Sjl 	return (0);
80125cf1a30Sjl }
80225cf1a30Sjl 
8030cc8ae86Sav /*
8040cc8ae86Sav  * UNUM format for DC is "/CMUnn/MEMxyZ", where
8050cc8ae86Sav  *	nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3.
8060cc8ae86Sav  *	x = MAC 0..3
8070cc8ae86Sav  *	y = 0..3 (slot info).
8080cc8ae86Sav  *	Z = 'A' or 'B'
8090cc8ae86Sav  *
8100cc8ae86Sav  * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where
8110cc8ae86Sav  *	x = 0..3 (MEMB number)
8120cc8ae86Sav  *	y = 0..3 (slot info).
8130cc8ae86Sav  *	Z = 'A' or 'B'
8140cc8ae86Sav  *
8150cc8ae86Sav  * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ"
8160cc8ae86Sav  *	x = 0..7 (MEMB number)
8170cc8ae86Sav  *	y = 0..3 (slot info).
8180cc8ae86Sav  *	Z = 'A' or 'B'
8190cc8ae86Sav  */
8200cc8ae86Sav int
821aeb241b2Sav mc_set_mem_unum(char *buf, int buflen, int sb, int bank,
8220cc8ae86Sav     uint32_t mf_type, uint32_t d_slot)
8230cc8ae86Sav {
8240cc8ae86Sav 	char *dimmnm;
8250cc8ae86Sav 	char memb_num;
826aeb241b2Sav 	int cs;
8270cc8ae86Sav 	int i;
828aeb241b2Sav 	int j;
8290cc8ae86Sav 
830aeb241b2Sav 	cs = SLOT_TO_CS(d_slot);
8310cc8ae86Sav 
8320cc8ae86Sav 	if (plat_model == MODEL_DC) {
8330cc8ae86Sav 		if (mf_type == FLT_TYPE_PERMANENT_CE) {
8340cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
8350cc8ae86Sav 			dimmnm = mc_dc_dimm_unum_table[i];
8360cc8ae86Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s",
8370cc8ae86Sav 			    model_names[plat_model].unit_name, sb, dimmnm);
8380cc8ae86Sav 		} else {
8390cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
840aeb241b2Sav 			j = (cs == 0) ?  i : i + 2;
841aeb241b2Sav 			snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
8420cc8ae86Sav 			    model_names[plat_model].unit_name, sb,
843aeb241b2Sav 			    mc_dc_dimm_unum_table[j],
844aeb241b2Sav 			    mc_dc_dimm_unum_table[j + 1]);
8450cc8ae86Sav 		}
8460cc8ae86Sav 	} else {
8470cc8ae86Sav 		if (mf_type == FLT_TYPE_PERMANENT_CE) {
848aeb241b2Sav 			i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
8490cc8ae86Sav 			dimmnm = mc_ff_dimm_unum_table[i];
8500cc8ae86Sav 			memb_num = dimmnm[0];
8510cc8ae86Sav 			snprintf(buf, buflen, "/%s/%s%c/MEM%s",
8520cc8ae86Sav 			    model_names[plat_model].unit_name,
8530cc8ae86Sav 			    model_names[plat_model].mem_name,
8540cc8ae86Sav 			    memb_num, &dimmnm[1]);
8550cc8ae86Sav 		} else {
8560cc8ae86Sav 			i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
857aeb241b2Sav 			j = (cs == 0) ?  i : i + 2;
8580cc8ae86Sav 			memb_num = mc_ff_dimm_unum_table[i][0],
859*d8a0cca9Swh 			    snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s",
8600cc8ae86Sav 			    model_names[plat_model].unit_name,
8610cc8ae86Sav 			    model_names[plat_model].mem_name, memb_num,
862aeb241b2Sav 			    &mc_ff_dimm_unum_table[j][1],
863aeb241b2Sav 			    &mc_ff_dimm_unum_table[j + 1][1]);
8640cc8ae86Sav 		}
8650cc8ae86Sav 	}
8660cc8ae86Sav 	return (0);
8670cc8ae86Sav }
8680cc8ae86Sav 
86925cf1a30Sjl static void
87025cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt)
87125cf1a30Sjl {
87225cf1a30Sjl 	char buf[FM_MAX_CLASS];
87325cf1a30Sjl 	char device_path[MAXPATHLEN];
8740cc8ae86Sav 	char sid[MAXPATHLEN];
87525cf1a30Sjl 	nv_alloc_t *nva = NULL;
87625cf1a30Sjl 	nvlist_t *ereport, *detector, *resource;
87725cf1a30Sjl 	errorq_elem_t *eqep;
87825cf1a30Sjl 	int nflts;
87925cf1a30Sjl 	mc_flt_stat_t *flt_stat;
8800cc8ae86Sav 	int i, n;
8810cc8ae86Sav 	int blen = MAXPATHLEN;
8820cc8ae86Sav 	char *p, *s = NULL;
88325cf1a30Sjl 	uint32_t values[2], synd[2], dslot[2];
8840cc8ae86Sav 	uint64_t offset = (uint64_t)-1;
8850cc8ae86Sav 	int ret = -1;
88625cf1a30Sjl 
88725cf1a30Sjl 	if (panicstr) {
88825cf1a30Sjl 		eqep = errorq_reserve(ereport_errorq);
88925cf1a30Sjl 		if (eqep == NULL)
89025cf1a30Sjl 			return;
89125cf1a30Sjl 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
89225cf1a30Sjl 		nva = errorq_elem_nva(ereport_errorq, eqep);
89325cf1a30Sjl 	} else {
89425cf1a30Sjl 		ereport = fm_nvlist_create(nva);
89525cf1a30Sjl 	}
89625cf1a30Sjl 
89725cf1a30Sjl 	/*
89825cf1a30Sjl 	 * Create the scheme "dev" FMRI.
89925cf1a30Sjl 	 */
90025cf1a30Sjl 	detector = fm_nvlist_create(nva);
90125cf1a30Sjl 	resource = fm_nvlist_create(nva);
90225cf1a30Sjl 
90325cf1a30Sjl 	nflts = mc_aflt->mflt_nflts;
90425cf1a30Sjl 
90525cf1a30Sjl 	ASSERT(nflts >= 1 && nflts <= 2);
90625cf1a30Sjl 
90725cf1a30Sjl 	flt_stat = mc_aflt->mflt_stat[0];
90825cf1a30Sjl 	(void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path);
90925cf1a30Sjl 	(void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
91025cf1a30Sjl 	    device_path, NULL);
91125cf1a30Sjl 
91225cf1a30Sjl 	/*
91325cf1a30Sjl 	 * Encode all the common data into the ereport.
91425cf1a30Sjl 	 */
915*d8a0cca9Swh 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", MC_OPL_ERROR_CLASS,
916*d8a0cca9Swh 	    mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : MC_OPL_MI_SUBCLASS,
917*d8a0cca9Swh 	    mc_aflt->mflt_erpt_class);
91825cf1a30Sjl 
91925cf1a30Sjl 	MC_LOG("mc_ereport_post: ereport %s\n", buf);
92025cf1a30Sjl 
92125cf1a30Sjl 
92225cf1a30Sjl 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
923*d8a0cca9Swh 	    fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), detector, NULL);
92425cf1a30Sjl 
92525cf1a30Sjl 	/*
92625cf1a30Sjl 	 * Set payload.
92725cf1a30Sjl 	 */
92825cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32,
929*d8a0cca9Swh 	    flt_stat->mf_flt_maddr.ma_bd, NULL);
93025cf1a30Sjl 
93125cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64,
932*d8a0cca9Swh 	    flt_stat->mf_flt_paddr, NULL);
93325cf1a30Sjl 
93425cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
935*d8a0cca9Swh 		fm_payload_set(ereport, MC_OPL_FLT_TYPE, DATA_TYPE_UINT8,
936*d8a0cca9Swh 		    ECC_STICKY, NULL);
93725cf1a30Sjl 	}
93825cf1a30Sjl 
93925cf1a30Sjl 	for (i = 0; i < nflts; i++)
94025cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank;
94125cf1a30Sjl 
942*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, nflts,
943*d8a0cca9Swh 	    values, NULL);
94425cf1a30Sjl 
94525cf1a30Sjl 	for (i = 0; i < nflts; i++)
94625cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_cntl;
94725cf1a30Sjl 
948*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, nflts,
949*d8a0cca9Swh 	    values, NULL);
95025cf1a30Sjl 
95125cf1a30Sjl 	for (i = 0; i < nflts; i++)
95225cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_add;
95325cf1a30Sjl 
9540cc8ae86Sav 	/* offset is set only for PCE */
9550cc8ae86Sav 	if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) {
9560cc8ae86Sav 		offset = values[0];
9570cc8ae86Sav 
9580cc8ae86Sav 	}
959*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, nflts,
960*d8a0cca9Swh 	    values, NULL);
96125cf1a30Sjl 
96225cf1a30Sjl 	for (i = 0; i < nflts; i++)
96325cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_log;
96425cf1a30Sjl 
965*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, nflts,
966*d8a0cca9Swh 	    values, NULL);
96725cf1a30Sjl 
96825cf1a30Sjl 	for (i = 0; i < nflts; i++) {
96925cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
97025cf1a30Sjl 		if (flt_stat->mf_errlog_valid) {
97125cf1a30Sjl 			synd[i] = flt_stat->mf_synd;
97225cf1a30Sjl 			dslot[i] = flt_stat->mf_dimm_slot;
97325cf1a30Sjl 			values[i] = flt_stat->mf_dram_place;
97425cf1a30Sjl 		} else {
97525cf1a30Sjl 			synd[i] = 0;
97625cf1a30Sjl 			dslot[i] = 0;
97725cf1a30Sjl 			values[i] = 0;
97825cf1a30Sjl 		}
97925cf1a30Sjl 	}
98025cf1a30Sjl 
981*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_SYND, DATA_TYPE_UINT32_ARRAY, nflts,
982*d8a0cca9Swh 	    synd, NULL);
98325cf1a30Sjl 
984*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, DATA_TYPE_UINT32_ARRAY,
985*d8a0cca9Swh 	    nflts, dslot, NULL);
98625cf1a30Sjl 
987*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_ERR_DRAM, DATA_TYPE_UINT32_ARRAY, nflts,
988*d8a0cca9Swh 	    values, NULL);
98925cf1a30Sjl 
99025cf1a30Sjl 	device_path[0] = 0;
99125cf1a30Sjl 	p = &device_path[0];
9920cc8ae86Sav 	sid[0] = 0;
9930cc8ae86Sav 	s = &sid[0];
9940cc8ae86Sav 	ret = 0;
99525cf1a30Sjl 
99625cf1a30Sjl 	for (i = 0; i < nflts; i++) {
9970cc8ae86Sav 		int bank;
99825cf1a30Sjl 
99925cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
10000cc8ae86Sav 		bank = flt_stat->mf_flt_maddr.ma_bank;
1001*d8a0cca9Swh 		ret = mc_set_mem_unum(p + strlen(p), blen,
1002*d8a0cca9Swh 		    flt_stat->mf_flt_maddr.ma_phys_bd, bank, flt_stat->mf_type,
1003*d8a0cca9Swh 		    flt_stat->mf_dimm_slot);
10040cc8ae86Sav 
10050cc8ae86Sav 		if (ret != 0) {
10060cc8ae86Sav 			cmn_err(CE_WARN,
10070cc8ae86Sav 			    "mc_ereport_post: Failed to determine the unum "
10080cc8ae86Sav 			    "for board=%d bank=%d type=0x%x slot=0x%x",
10090cc8ae86Sav 			    flt_stat->mf_flt_maddr.ma_bd, bank,
10100cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10110cc8ae86Sav 			continue;
101225cf1a30Sjl 		}
10130cc8ae86Sav 		n = strlen(device_path);
101425cf1a30Sjl 		blen = MAXPATHLEN - n;
101525cf1a30Sjl 		p = &device_path[n];
101625cf1a30Sjl 		if (i < (nflts - 1)) {
101725cf1a30Sjl 			snprintf(p, blen, " ");
10180cc8ae86Sav 			blen--;
10190cc8ae86Sav 			p++;
10200cc8ae86Sav 		}
10210cc8ae86Sav 
10220cc8ae86Sav 		if (ret == 0) {
10230cc8ae86Sav 			ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s),
1024aeb241b2Sav 			    blen, flt_stat->mf_flt_maddr.ma_phys_bd, bank,
10250cc8ae86Sav 			    flt_stat->mf_type, flt_stat->mf_dimm_slot);
10260cc8ae86Sav 
102725cf1a30Sjl 		}
102825cf1a30Sjl 	}
102925cf1a30Sjl 
1030*d8a0cca9Swh 	(void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, NULL,
1031*d8a0cca9Swh 	    device_path, (ret == 0) ? sid : NULL, (ret == 0) ? offset :
1032*d8a0cca9Swh 	    (uint64_t)-1);
103325cf1a30Sjl 
1034*d8a0cca9Swh 	fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, resource,
1035*d8a0cca9Swh 	    NULL);
103625cf1a30Sjl 
103725cf1a30Sjl 	if (panicstr) {
103825cf1a30Sjl 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
103925cf1a30Sjl 	} else {
104025cf1a30Sjl 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
104125cf1a30Sjl 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
104225cf1a30Sjl 		fm_nvlist_destroy(detector, FM_NVA_FREE);
104325cf1a30Sjl 		fm_nvlist_destroy(resource, FM_NVA_FREE);
104425cf1a30Sjl 	}
104525cf1a30Sjl }
104625cf1a30Sjl 
10470cc8ae86Sav 
104825cf1a30Sjl static void
104925cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt)
105025cf1a30Sjl {
105125cf1a30Sjl 	int rv;
105225cf1a30Sjl 	uint64_t pa = (uint64_t)(-1);
10530cc8ae86Sav 	int i;
105425cf1a30Sjl 
1055*d8a0cca9Swh 	MC_LOG("mc_err_drain: %s\n", mc_aflt->mflt_erpt_class);
105625cf1a30Sjl 	/*
105725cf1a30Sjl 	 * we come here only when we have:
10581039f409Sav 	 * In mirror mode: MUE, SUE
105925cf1a30Sjl 	 * In normal mode: UE, Permanent CE
106025cf1a30Sjl 	 */
10610cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
10620cc8ae86Sav 		rv = mcaddr_to_pa(mc_aflt->mflt_mcp,
1063*d8a0cca9Swh 		    &(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa);
1064738dd194Shyw 
1065738dd194Shyw 		/* Ensure the pa is valid (not in isolated memory block) */
1066738dd194Shyw 		if (rv == 0 && pa_is_valid(mc_aflt->mflt_mcp, pa))
10670cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = pa;
10680cc8ae86Sav 		else
10690cc8ae86Sav 			mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1;
10700cc8ae86Sav 	}
10710cc8ae86Sav 
1072738dd194Shyw 	MC_LOG("mc_err_drain:pa = %lx\n", pa);
107325cf1a30Sjl 
1074738dd194Shyw 	switch (page_retire_check(pa, NULL)) {
1075738dd194Shyw 	case 0:
1076738dd194Shyw 	case EAGAIN:
1077738dd194Shyw 		MC_LOG("Page retired or pending\n");
1078738dd194Shyw 		return;
1079738dd194Shyw 	case EIO:
1080738dd194Shyw 		/*
1081738dd194Shyw 		 * Do page retirement except for the PCE case.
1082738dd194Shyw 		 * This is taken care by the OPL DE
1083738dd194Shyw 		 */
1084738dd194Shyw 		if (mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) {
1085738dd194Shyw 			MC_LOG("offline page at pa %lx error %x\n", pa,
1086*d8a0cca9Swh 			    mc_aflt->mflt_pr);
1087738dd194Shyw 			(void) page_retire(pa, mc_aflt->mflt_pr);
108825cf1a30Sjl 		}
1089738dd194Shyw 		break;
1090738dd194Shyw 	case EINVAL:
1091738dd194Shyw 	default:
1092738dd194Shyw 		/*
1093738dd194Shyw 		 * Some memory do not have page structure so
1094738dd194Shyw 		 * we keep going in case of EINVAL.
1095738dd194Shyw 		 */
1096738dd194Shyw 		break;
109725cf1a30Sjl 	}
109825cf1a30Sjl 
10990cc8ae86Sav 	for (i = 0; i < mc_aflt->mflt_nflts; i++) {
11000cc8ae86Sav 		mc_aflt_t mc_aflt0;
11010cc8ae86Sav 		if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) {
11020cc8ae86Sav 			mc_aflt0 = *mc_aflt;
11030cc8ae86Sav 			mc_aflt0.mflt_nflts = 1;
11040cc8ae86Sav 			mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i];
11050cc8ae86Sav 			mc_ereport_post(&mc_aflt0);
11060cc8ae86Sav 		}
11070cc8ae86Sav 	}
11080cc8ae86Sav }
110925cf1a30Sjl 
111025cf1a30Sjl /*
111125cf1a30Sjl  * The restart address is actually defined in unit of PA[37:6]
111225cf1a30Sjl  * the mac patrol will convert that to dimm offset.  If the
111325cf1a30Sjl  * address is not in the bank, it will continue to search for
111425cf1a30Sjl  * the next PA that is within the bank.
111525cf1a30Sjl  *
111625cf1a30Sjl  * Also the mac patrol scans the dimms based on PA, not
111725cf1a30Sjl  * dimm offset.
111825cf1a30Sjl  */
111925cf1a30Sjl static int
1120738dd194Shyw restart_patrol(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr_info)
112125cf1a30Sjl {
112225cf1a30Sjl 	uint64_t pa;
112325cf1a30Sjl 	int rv;
112425cf1a30Sjl 
1125738dd194Shyw 	if (rsaddr_info == NULL || (rsaddr_info->mi_valid == 0)) {
112625cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
112725cf1a30Sjl 		return (0);
112825cf1a30Sjl 	}
112925cf1a30Sjl 
1130738dd194Shyw 	rv = mcaddr_to_pa(mcp, &rsaddr_info->mi_restartaddr, &pa);
113125cf1a30Sjl 	if (rv != 0) {
113225cf1a30Sjl 		MC_LOG("cannot convert mcaddr to pa. use auto restart\n");
113325cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
113425cf1a30Sjl 		return (0);
113525cf1a30Sjl 	}
113625cf1a30Sjl 
1137738dd194Shyw 	if (!mc_rangecheck_pa(mcp, pa)) {
113825cf1a30Sjl 		/* pa is not on this board, just retry */
113925cf1a30Sjl 		cmn_err(CE_WARN, "restart_patrol: invalid address %lx "
1140*d8a0cca9Swh 		    "on board %d\n", pa, mcp->mc_board_num);
114125cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
114225cf1a30Sjl 		return (0);
114325cf1a30Sjl 	}
114425cf1a30Sjl 
114525cf1a30Sjl 	MC_LOG("restart_patrol: pa = %lx\n", pa);
114625cf1a30Sjl 
1147738dd194Shyw 	if (!rsaddr_info->mi_injectrestart) {
1148738dd194Shyw 		/*
11491039f409Sav 		 * For non-error injection restart we need to
1150738dd194Shyw 		 * determine if the current restart pa/page is
1151738dd194Shyw 		 * a "good" page. A "good" page is a page that
1152738dd194Shyw 		 * has not been page retired. If the current
1153738dd194Shyw 		 * page that contains the pa is "good", we will
1154738dd194Shyw 		 * do a HW auto restart and let HW patrol continue
1155738dd194Shyw 		 * where it last stopped. Most desired scenario.
1156738dd194Shyw 		 *
1157738dd194Shyw 		 * If the current page is not "good", we will advance
1158738dd194Shyw 		 * to the next page to find the next "good" page and
1159738dd194Shyw 		 * restart the patrol from there.
1160738dd194Shyw 		 */
1161738dd194Shyw 		int wrapcount = 0;
1162738dd194Shyw 		uint64_t origpa = pa;
1163738dd194Shyw 		while (wrapcount < 2) {
1164*d8a0cca9Swh 			if (!pa_is_valid(mcp, pa)) {
1165738dd194Shyw 				/*
1166*d8a0cca9Swh 				 * Not in physinstall - advance to the
1167*d8a0cca9Swh 				 * next memory isolation blocksize
1168738dd194Shyw 				 */
1169*d8a0cca9Swh 				MC_LOG("Invalid PA\n");
1170*d8a0cca9Swh 				pa = roundup(pa + 1, mc_isolation_bsize);
1171*d8a0cca9Swh 			} else {
1172*d8a0cca9Swh 				int rv;
1173*d8a0cca9Swh 				if ((rv = page_retire_check(pa, NULL)) != 0 &&
1174*d8a0cca9Swh 				    rv != EAGAIN) {
1175*d8a0cca9Swh 					/*
1176*d8a0cca9Swh 					 * The page is "good" (not retired),
1177*d8a0cca9Swh 					 * we will use automatic HW restart
1178*d8a0cca9Swh 					 * algorithm if this is the original
1179*d8a0cca9Swh 					 * current starting page.
1180*d8a0cca9Swh 					 */
1181*d8a0cca9Swh 					if (pa == origpa) {
1182*d8a0cca9Swh 						MC_LOG("Page has no error. "
1183*d8a0cca9Swh 						    "Auto restart\n");
1184*d8a0cca9Swh 						MAC_PTRL_START(mcp, bank);
1185*d8a0cca9Swh 						return (0);
1186*d8a0cca9Swh 					} else {
1187*d8a0cca9Swh 						/*
1188*d8a0cca9Swh 						 * found a subsequent good page
1189*d8a0cca9Swh 						 */
1190*d8a0cca9Swh 						break;
1191*d8a0cca9Swh 					}
1192738dd194Shyw 				}
1193738dd194Shyw 
1194*d8a0cca9Swh 				/*
1195*d8a0cca9Swh 				 * Skip to the next page
1196*d8a0cca9Swh 				 */
1197*d8a0cca9Swh 				pa = roundup(pa + 1, PAGESIZE);
1198*d8a0cca9Swh 				MC_LOG("Skipping bad page to %lx\n", pa);
1199*d8a0cca9Swh 			}
1200738dd194Shyw 
1201*d8a0cca9Swh 			/* Check to see if we hit the end of the memory range */
1202*d8a0cca9Swh 			if (pa >= (mcp->mc_start_address + mcp->mc_size)) {
1203*d8a0cca9Swh 				MC_LOG("Wrap around\n");
1204*d8a0cca9Swh 				pa = mcp->mc_start_address;
1205*d8a0cca9Swh 				wrapcount++;
1206*d8a0cca9Swh 			}
1207738dd194Shyw 		}
1208738dd194Shyw 
1209738dd194Shyw 		if (wrapcount > 1) {
1210*d8a0cca9Swh 			MC_LOG("Failed to find a good page. Just restart\n");
1211*d8a0cca9Swh 			MAC_PTRL_START(mcp, bank);
1212*d8a0cca9Swh 			return (0);
121325cf1a30Sjl 		}
121425cf1a30Sjl 	}
121525cf1a30Sjl 
1216738dd194Shyw 	/*
1217738dd194Shyw 	 * We reached here either:
1218738dd194Shyw 	 * 1. We are doing an error injection restart that specify
1219738dd194Shyw 	 *    the exact pa/page to restart. OR
1220738dd194Shyw 	 * 2. We found a subsequent good page different from the
1221738dd194Shyw 	 *    original restart pa/page.
1222738dd194Shyw 	 * Restart MAC patrol: PA[37:6]
1223738dd194Shyw 	 */
122425cf1a30Sjl 	MC_LOG("restart at pa = %lx\n", pa);
122525cf1a30Sjl 	ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa));
122625cf1a30Sjl 	MAC_PTRL_START_ADD(mcp, bank);
122725cf1a30Sjl 
122825cf1a30Sjl 	return (0);
122925cf1a30Sjl }
123025cf1a30Sjl 
123125cf1a30Sjl /*
123225cf1a30Sjl  * Rewriting is used for two purposes.
123325cf1a30Sjl  *  - to correct the error in memory.
123425cf1a30Sjl  *  - to determine whether the error is permanent or intermittent.
123525cf1a30Sjl  * It's done by writing the address in MAC_BANKm_REWRITE_ADD
123625cf1a30Sjl  * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that,
123725cf1a30Sjl  * REW_END (and REW_CE/REW_UE if some error detected) is set when
123825cf1a30Sjl  * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM.
123925cf1a30Sjl  *
124025cf1a30Sjl  * Note that rewrite operation doesn't change RAW_UE to Marked UE.
124125cf1a30Sjl  * Therefore, we use it only CE case.
124225cf1a30Sjl  */
124325cf1a30Sjl static uint32_t
124425cf1a30Sjl do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr)
124525cf1a30Sjl {
124625cf1a30Sjl 	uint32_t cntl;
124725cf1a30Sjl 	int count = 0;
124825cf1a30Sjl 
124925cf1a30Sjl 	/* first wait to make sure PTRL_STATUS is 0 */
12500cc8ae86Sav 	while (count++ < mc_max_rewrite_loop) {
125125cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
125225cf1a30Sjl 		if (!(cntl & MAC_CNTL_PTRL_STATUS))
125325cf1a30Sjl 			break;
12540cc8ae86Sav 		drv_usecwait(mc_rewrite_delay);
125525cf1a30Sjl 	}
12560cc8ae86Sav 	if (count >= mc_max_rewrite_loop)
125725cf1a30Sjl 		goto bad;
125825cf1a30Sjl 
125925cf1a30Sjl 	count = 0;
126025cf1a30Sjl 
126125cf1a30Sjl 	ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr);
126225cf1a30Sjl 	MAC_REW_REQ(mcp, bank);
126325cf1a30Sjl 
126425cf1a30Sjl 	do {
126525cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
12660cc8ae86Sav 		if (count++ >= mc_max_rewrite_loop) {
126725cf1a30Sjl 			goto bad;
12680cc8ae86Sav 		} else {
12690cc8ae86Sav 			drv_usecwait(mc_rewrite_delay);
12700cc8ae86Sav 		}
127125cf1a30Sjl 	/*
127225cf1a30Sjl 	 * If there are other MEMORY or PCI activities, this
127325cf1a30Sjl 	 * will be BUSY, else it should be set immediately
127425cf1a30Sjl 	 */
127525cf1a30Sjl 	} while (!(cntl & MAC_CNTL_REW_END));
127625cf1a30Sjl 
127725cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
127825cf1a30Sjl 	return (cntl);
127925cf1a30Sjl bad:
128025cf1a30Sjl 	/* This is bad.  Just reset the circuit */
128125cf1a30Sjl 	cmn_err(CE_WARN, "mc-opl rewrite timeout on /LSB%d/B%d\n",
1282*d8a0cca9Swh 	    mcp->mc_board_num, bank);
128325cf1a30Sjl 	cntl = MAC_CNTL_REW_END;
128425cf1a30Sjl 	MAC_CMD(mcp, bank, MAC_CNTL_PTRL_RESET);
128525cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
128625cf1a30Sjl 	return (cntl);
128725cf1a30Sjl }
128825cf1a30Sjl void
128925cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp)
129025cf1a30Sjl {
12910cc8ae86Sav 	int count;
12920cc8ae86Sav 	int n = 0;
129325cf1a30Sjl 	scf_log_t *p;
129425cf1a30Sjl 	int bank;
129525cf1a30Sjl 
12960cc8ae86Sav 	for (bank = 0; bank < BANKNUM_PER_SB; bank++) {
1297*d8a0cca9Swh 		while ((p = mcp->mc_scf_log[bank]) != NULL &&
1298*d8a0cca9Swh 		    (n < mc_max_errlog_processed)) {
1299*d8a0cca9Swh 			ASSERT(bank == p->sl_bank);
1300*d8a0cca9Swh 			count = 0;
1301*d8a0cca9Swh 			while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank))
1302*d8a0cca9Swh 			    & MAC_STATIC_ERR_VLD)) {
1303*d8a0cca9Swh 				if (count++ >= (mc_max_scf_loop)) {
1304*d8a0cca9Swh 					break;
1305*d8a0cca9Swh 				}
1306*d8a0cca9Swh 				drv_usecwait(mc_scf_delay);
130725cf1a30Sjl 			}
130825cf1a30Sjl 
1309*d8a0cca9Swh 			if (count < mc_max_scf_loop) {
1310*d8a0cca9Swh 				ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank),
1311*d8a0cca9Swh 				    p->sl_err_log);
131225cf1a30Sjl 
1313*d8a0cca9Swh 				ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank),
1314*d8a0cca9Swh 				    p->sl_err_add|MAC_STATIC_ERR_VLD);
1315*d8a0cca9Swh 				mcp->mc_scf_retry[bank] = 0;
131625cf1a30Sjl 			} else {
1317*d8a0cca9Swh 				/*
1318*d8a0cca9Swh 				 * if we try too many times, just drop the req
1319*d8a0cca9Swh 				 */
1320*d8a0cca9Swh 				if (mcp->mc_scf_retry[bank]++ <=
1321*d8a0cca9Swh 				    mc_max_scf_retry) {
1322*d8a0cca9Swh 					return;
1323*d8a0cca9Swh 				} else {
1324*d8a0cca9Swh 					if ((++mc_pce_dropped & 0xff) == 0) {
1325*d8a0cca9Swh 						cmn_err(CE_WARN, "Cannot "
1326*d8a0cca9Swh 						    "report Permanent CE to "
1327*d8a0cca9Swh 						    "SCF\n");
1328*d8a0cca9Swh 					}
1329*d8a0cca9Swh 				}
133025cf1a30Sjl 			}
1331*d8a0cca9Swh 			n++;
1332*d8a0cca9Swh 			mcp->mc_scf_log[bank] = p->sl_next;
1333*d8a0cca9Swh 			mcp->mc_scf_total[bank]--;
1334*d8a0cca9Swh 			ASSERT(mcp->mc_scf_total[bank] >= 0);
1335*d8a0cca9Swh 			kmem_free(p, sizeof (scf_log_t));
133625cf1a30Sjl 		}
133725cf1a30Sjl 	}
133825cf1a30Sjl }
133925cf1a30Sjl void
134025cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank)
134125cf1a30Sjl {
134225cf1a30Sjl 	scf_log_t *p;
134325cf1a30Sjl 
13440cc8ae86Sav 	if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) {
13450cc8ae86Sav 		if ((++mc_pce_dropped & 0xff) == 0) {
1346*d8a0cca9Swh 			cmn_err(CE_WARN, "Too many Permanent CE requests.\n");
13470cc8ae86Sav 		}
134825cf1a30Sjl 		return;
134925cf1a30Sjl 	}
135025cf1a30Sjl 	p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP);
135125cf1a30Sjl 	p->sl_next = 0;
135225cf1a30Sjl 	p->sl_err_add = flt_stat->mf_err_add;
135325cf1a30Sjl 	p->sl_err_log = flt_stat->mf_err_log;
135425cf1a30Sjl 	p->sl_bank = bank;
135525cf1a30Sjl 
13560cc8ae86Sav 	if (mcp->mc_scf_log[bank] == NULL) {
135725cf1a30Sjl 		/*
135825cf1a30Sjl 		 * we rely on mc_scf_log to detect NULL queue.
135925cf1a30Sjl 		 * mc_scf_log_tail is irrelevant is such case.
136025cf1a30Sjl 		 */
13610cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p;
136225cf1a30Sjl 	} else {
13630cc8ae86Sav 		mcp->mc_scf_log_tail[bank]->sl_next = p;
13640cc8ae86Sav 		mcp->mc_scf_log_tail[bank] = p;
136525cf1a30Sjl 	}
13660cc8ae86Sav 	mcp->mc_scf_total[bank]++;
136725cf1a30Sjl }
136825cf1a30Sjl /*
136925cf1a30Sjl  * This routine determines what kind of CE happens, intermittent
137025cf1a30Sjl  * or permanent as follows. (See 4.7.3 in Columbus2 PRM.)
137125cf1a30Sjl  * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register.
137225cf1a30Sjl  * - If CE is still detected on the same address even after doing
137325cf1a30Sjl  *   rewrite operation twice, it is determined as permanent error.
137425cf1a30Sjl  * - If error is not detected anymore, it is determined as intermittent
137525cf1a30Sjl  *   error.
137625cf1a30Sjl  * - If UE is detected due to rewrite operation, it should be treated
137725cf1a30Sjl  *   as UE.
137825cf1a30Sjl  */
137925cf1a30Sjl 
138025cf1a30Sjl /* ARGSUSED */
138125cf1a30Sjl static void
138225cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error)
138325cf1a30Sjl {
138425cf1a30Sjl 	uint32_t cntl;
138525cf1a30Sjl 	int i;
138625cf1a30Sjl 
138725cf1a30Sjl 	flt_stat->mf_type = FLT_TYPE_PERMANENT_CE;
138825cf1a30Sjl 	/*
138925cf1a30Sjl 	 * rewrite request 1st time reads and correct error data
139025cf1a30Sjl 	 * and write to DIMM.  2nd rewrite request must be issued
139125cf1a30Sjl 	 * after REW_CE/UE/END is 0.  When the 2nd request is completed,
139225cf1a30Sjl 	 * if REW_CE = 1, then it is permanent CE.
139325cf1a30Sjl 	 */
139425cf1a30Sjl 	for (i = 0; i < 2; i++) {
139525cf1a30Sjl 		cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add);
139625cf1a30Sjl 		/*
139725cf1a30Sjl 		 * If the error becomes UE or CMPE
139825cf1a30Sjl 		 * we return to the caller immediately.
139925cf1a30Sjl 		 */
140025cf1a30Sjl 		if (cntl & MAC_CNTL_REW_UE) {
140125cf1a30Sjl 			if (ptrl_error)
140225cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE;
140325cf1a30Sjl 			else
140425cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_UE;
140525cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_UE;
140625cf1a30Sjl 			return;
140725cf1a30Sjl 		}
140825cf1a30Sjl 		if (cntl & MAC_CNTL_REW_CMPE) {
140925cf1a30Sjl 			if (ptrl_error)
141025cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE;
141125cf1a30Sjl 			else
141225cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE;
141325cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_CMPE;
141425cf1a30Sjl 			return;
141525cf1a30Sjl 		}
141625cf1a30Sjl 	}
141725cf1a30Sjl 	if (!(cntl & MAC_CNTL_REW_CE)) {
141825cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE;
141925cf1a30Sjl 	}
142025cf1a30Sjl 
142125cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
142225cf1a30Sjl 		/* report PERMANENT_CE to SP via SCF */
142325cf1a30Sjl 		if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) {
142425cf1a30Sjl 			mc_queue_scf_log(mcp, flt_stat, bank);
142525cf1a30Sjl 		}
142625cf1a30Sjl 	}
142725cf1a30Sjl }
142825cf1a30Sjl 
142925cf1a30Sjl #define	IS_CMPE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\
143025cf1a30Sjl 				MAC_CNTL_MI_CMPE))
143125cf1a30Sjl #define	IS_UE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE))
143225cf1a30Sjl #define	IS_CE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE))
143325cf1a30Sjl #define	IS_OK(cntl, f)	(!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \
143425cf1a30Sjl 			MAC_CNTL_MI_ERRS)))
143525cf1a30Sjl 
143625cf1a30Sjl 
143725cf1a30Sjl static int
143825cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error)
143925cf1a30Sjl {
144025cf1a30Sjl 	if (ptrl_error) {
144125cf1a30Sjl 		return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE);
144225cf1a30Sjl 	} else {
144325cf1a30Sjl 		return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE);
144425cf1a30Sjl 	}
144525cf1a30Sjl }
144625cf1a30Sjl 
144725cf1a30Sjl void
144825cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value)
144925cf1a30Sjl {
145037afe445Shyw 	int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
145137afe445Shyw 
145237afe445Shyw 	if (mcp->mc_speedup_period[ebank] > 0)
14530cc8ae86Sav 		value |= mc_max_speed;
14540cc8ae86Sav 	else
14550cc8ae86Sav 		value |= mcp->mc_speed;
145625cf1a30Sjl 	ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value);
145725cf1a30Sjl }
145825cf1a30Sjl 
145925cf1a30Sjl static void
146025cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
146125cf1a30Sjl {
146225cf1a30Sjl 	flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
1463*d8a0cca9Swh 	    MAC_CNTL_PTRL_ERRS;
146425cf1a30Sjl 	flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank));
146525cf1a30Sjl 	flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank));
146625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
1467aeb241b2Sav 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
146825cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
146925cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
147025cf1a30Sjl }
147125cf1a30Sjl 
147225cf1a30Sjl static void
147325cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
147425cf1a30Sjl {
147525cf1a30Sjl 	uint32_t status, old_status;
147625cf1a30Sjl 
1477*d8a0cca9Swh 	status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & MAC_CNTL_MI_ERRS;
147825cf1a30Sjl 	old_status = 0;
147925cf1a30Sjl 
148025cf1a30Sjl 	/* we keep reading until the status is stable */
148125cf1a30Sjl 	while (old_status != status) {
148225cf1a30Sjl 		old_status = status;
1483*d8a0cca9Swh 		flt_stat->mf_err_add = LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank));
1484*d8a0cca9Swh 		flt_stat->mf_err_log = LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank));
148525cf1a30Sjl 		status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
1486*d8a0cca9Swh 		    MAC_CNTL_MI_ERRS;
148725cf1a30Sjl 		if (status == old_status) {
148825cf1a30Sjl 			break;
148925cf1a30Sjl 		}
149025cf1a30Sjl 	}
149125cf1a30Sjl 
149225cf1a30Sjl 	flt_stat->mf_cntl = status;
149325cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
1494aeb241b2Sav 	flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num;
149525cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
149625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
149725cf1a30Sjl }
149825cf1a30Sjl 
149925cf1a30Sjl 
150025cf1a30Sjl /*
150125cf1a30Sjl  * Error philosophy for mirror mode:
150225cf1a30Sjl  *
150325cf1a30Sjl  * PTRL (The error address for both banks are same, since ptrl stops if it
150425cf1a30Sjl  * detects error.)
15051039f409Sav  * - Compare error  log CMPE.
150625cf1a30Sjl  *
150725cf1a30Sjl  * - UE-UE           Report MUE.  No rewrite.
150825cf1a30Sjl  *
150925cf1a30Sjl  * - UE-*	     UE-(CE/OK). Rewrite to scrub UE.  Report SUE.
151025cf1a30Sjl  *
151125cf1a30Sjl  * - CE-*            CE-(CE/OK). Scrub to determine if CE is permanent.
151225cf1a30Sjl  *                   If CE is permanent, inform SCF.  Once for each
151325cf1a30Sjl  *		     Dimm.  If CE becomes UE or CMPE, go back to above.
151425cf1a30Sjl  *
151525cf1a30Sjl  *
151625cf1a30Sjl  * MI (The error addresses for each bank are the same or different.)
15171039f409Sav  * - Compare  error  If addresses are the same.  Just CMPE, so log CMPE.
151825cf1a30Sjl  *		     If addresses are different (this could happen
15191039f409Sav  *		     as a result of scrubbing.  Report each separately.
152025cf1a30Sjl  *		     Only report error info on each side.
152125cf1a30Sjl  *
152225cf1a30Sjl  * - UE-UE           Addresses are the same.  Report MUE.
152325cf1a30Sjl  *		     Addresses are different.  Report SUE on each bank.
152425cf1a30Sjl  *		     Rewrite to clear UE.
152525cf1a30Sjl  *
152625cf1a30Sjl  * - UE-*	     UE-(CE/OK)
152725cf1a30Sjl  *		     Rewrite to clear UE.  Report SUE for the bank.
152825cf1a30Sjl  *
152925cf1a30Sjl  * - CE-*            CE-(CE/OK).  Scrub to determine if CE is permanent.
153025cf1a30Sjl  *                   If CE becomes UE or CMPE, go back to above.
153125cf1a30Sjl  *
153225cf1a30Sjl  */
153325cf1a30Sjl 
153425cf1a30Sjl static int
153525cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat)
153625cf1a30Sjl {
153725cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
153825cf1a30Sjl 	int i;
153925cf1a30Sjl 	int rv = 0;
154025cf1a30Sjl 
154125cf1a30Sjl 	MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n",
1542*d8a0cca9Swh 	    flt_stat[0].mf_cntl, flt_stat[1].mf_cntl);
154325cf1a30Sjl 
154425cf1a30Sjl 	if (ptrl_error) {
1545*d8a0cca9Swh 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) &
1546*d8a0cca9Swh 		    MAC_CNTL_PTRL_ERRS) == 0)
154725cf1a30Sjl 			return (0);
154825cf1a30Sjl 	} else {
1549*d8a0cca9Swh 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) &
1550*d8a0cca9Swh 		    MAC_CNTL_MI_ERRS) == 0)
155125cf1a30Sjl 			return (0);
155225cf1a30Sjl 	}
155325cf1a30Sjl 
155425cf1a30Sjl 	/*
155525cf1a30Sjl 	 * First we take care of the case of CE
155625cf1a30Sjl 	 * because they can become UE or CMPE
155725cf1a30Sjl 	 */
155825cf1a30Sjl 	for (i = 0; i < 2; i++) {
155925cf1a30Sjl 		if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) {
156025cf1a30Sjl 			MC_LOG("CE detected on bank %d\n",
1561*d8a0cca9Swh 			    flt_stat[i].mf_flt_maddr.ma_bank);
156225cf1a30Sjl 			mc_scrub_ce(mcp, flt_stat[i].mf_flt_maddr.ma_bank,
1563*d8a0cca9Swh 			    &flt_stat[i], ptrl_error);
156425cf1a30Sjl 			rv = 1;
156525cf1a30Sjl 		}
156625cf1a30Sjl 	}
156725cf1a30Sjl 
156825cf1a30Sjl 	/* The above scrubbing can turn CE into UE or CMPE */
156925cf1a30Sjl 
157025cf1a30Sjl 	/*
157125cf1a30Sjl 	 * Now we distinguish two cases: same address or not
157225cf1a30Sjl 	 * the same address.  It might seem more intuitive to
157325cf1a30Sjl 	 * distinguish PTRL v.s. MI error but it is more
157425cf1a30Sjl 	 * complicated that way.
157525cf1a30Sjl 	 */
157625cf1a30Sjl 
157725cf1a30Sjl 	if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) {
157825cf1a30Sjl 
157925cf1a30Sjl 		if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) ||
158025cf1a30Sjl 		    IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) {
158125cf1a30Sjl 			flt_stat[0].mf_type = FLT_TYPE_CMPE;
158225cf1a30Sjl 			flt_stat[1].mf_type = FLT_TYPE_CMPE;
158325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
158425cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
158525cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
158625cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
158725cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
15881039f409Sav 			/*
15891039f409Sav 			 * Compare error is result of MAC internal error, so
15901039f409Sav 			 * simply log it instead of publishing an ereport. SCF
15911039f409Sav 			 * diagnoses all the MAC internal and its i/f error.
15921039f409Sav 			 * mc_err_drain(mc_aflt);
15931039f409Sav 			 */
15941039f409Sav 			MC_LOG("cmpe error detected\n");
159525cf1a30Sjl 			return (1);
159625cf1a30Sjl 		}
159725cf1a30Sjl 
159825cf1a30Sjl 		if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) &&
1599*d8a0cca9Swh 		    IS_UE(flt_stat[1].mf_cntl, ptrl_error)) {
160025cf1a30Sjl 			/* Both side are UE's */
160125cf1a30Sjl 
160225cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[0]);
160325cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[1]);
160425cf1a30Sjl 			MC_LOG("MUE detected\n");
16050cc8ae86Sav 			flt_stat[0].mf_type = FLT_TYPE_MUE;
16060cc8ae86Sav 			flt_stat[1].mf_type = FLT_TYPE_MUE;
160725cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_MUE;
160825cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
160925cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
161025cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
161125cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
161225cf1a30Sjl 			mc_err_drain(mc_aflt);
161325cf1a30Sjl 			return (1);
161425cf1a30Sjl 		}
161525cf1a30Sjl 
161625cf1a30Sjl 		/* Now the only case is UE/CE, UE/OK, or don't care */
161725cf1a30Sjl 		for (i = 0; i < 2; i++) {
1618*d8a0cca9Swh 			if (!IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
1619*d8a0cca9Swh 				continue;
1620*d8a0cca9Swh 			}
16210cc8ae86Sav 
16220cc8ae86Sav 			/* rewrite can clear the one side UE error */
16230cc8ae86Sav 
162425cf1a30Sjl 			if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) {
162525cf1a30Sjl 				(void) do_rewrite(mcp,
162625cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_bank,
162725cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr);
162825cf1a30Sjl 			}
162925cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
163025cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
163125cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
163225cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
163325cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
163425cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
163525cf1a30Sjl 			mc_err_drain(mc_aflt);
163625cf1a30Sjl 			/* Once we hit a UE/CE or UE/OK case, done */
163725cf1a30Sjl 			return (1);
163825cf1a30Sjl 		}
163925cf1a30Sjl 
164025cf1a30Sjl 	} else {
164125cf1a30Sjl 		/*
164225cf1a30Sjl 		 * addresses are different. That means errors
164325cf1a30Sjl 		 * on the 2 banks are not related at all.
164425cf1a30Sjl 		 */
164525cf1a30Sjl 		for (i = 0; i < 2; i++) {
1646*d8a0cca9Swh 			if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) {
1647*d8a0cca9Swh 				flt_stat[i].mf_type = FLT_TYPE_CMPE;
1648*d8a0cca9Swh 				mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
1649*d8a0cca9Swh 				mc_aflt->mflt_nflts = 1;
1650*d8a0cca9Swh 				mc_aflt->mflt_stat[0] = &flt_stat[i];
1651*d8a0cca9Swh 				mc_aflt->mflt_pr = PR_UE;
1652*d8a0cca9Swh 				/*
1653*d8a0cca9Swh 				 * Compare error is result of MAC internal
1654*d8a0cca9Swh 				 * error, so simply log it instead of
1655*d8a0cca9Swh 				 * publishing an ereport. SCF diagnoses all
1656*d8a0cca9Swh 				 * the MAC internal and its interface error.
1657*d8a0cca9Swh 				 * mc_err_drain(mc_aflt);
1658*d8a0cca9Swh 				 */
1659*d8a0cca9Swh 				MC_LOG("cmpe error detected\n");
1660*d8a0cca9Swh 				/* no more report on this bank */
1661*d8a0cca9Swh 				flt_stat[i].mf_cntl = 0;
1662*d8a0cca9Swh 				rv = 1;
1663*d8a0cca9Swh 			}
166425cf1a30Sjl 		}
166525cf1a30Sjl 
16660cc8ae86Sav 		/* rewrite can clear the one side UE error */
16670cc8ae86Sav 
166825cf1a30Sjl 		for (i = 0; i < 2; i++) {
1669*d8a0cca9Swh 			if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
1670*d8a0cca9Swh 				(void) do_rewrite(mcp,
1671*d8a0cca9Swh 				    flt_stat[i].mf_flt_maddr.ma_bank,
1672*d8a0cca9Swh 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr);
1673*d8a0cca9Swh 				flt_stat[i].mf_type = FLT_TYPE_UE;
1674*d8a0cca9Swh 				MAC_SET_ERRLOG_INFO(&flt_stat[i]);
1675*d8a0cca9Swh 				mc_aflt->mflt_erpt_class = MC_OPL_SUE;
1676*d8a0cca9Swh 				mc_aflt->mflt_stat[0] = &flt_stat[i];
1677*d8a0cca9Swh 				mc_aflt->mflt_nflts = 1;
1678*d8a0cca9Swh 				mc_aflt->mflt_pr = PR_MCE;
1679*d8a0cca9Swh 				mc_err_drain(mc_aflt);
1680*d8a0cca9Swh 				rv = 1;
1681*d8a0cca9Swh 			}
168225cf1a30Sjl 		}
168325cf1a30Sjl 	}
168425cf1a30Sjl 	return (rv);
168525cf1a30Sjl }
168625cf1a30Sjl static void
1687738dd194Shyw mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
168825cf1a30Sjl {
168925cf1a30Sjl 	mc_aflt_t mc_aflt;
169025cf1a30Sjl 	mc_flt_stat_t flt_stat[2], mi_flt_stat[2];
16910cc8ae86Sav 	int i;
16920cc8ae86Sav 	int mi_valid;
169325cf1a30Sjl 
1694738dd194Shyw 	ASSERT(rsaddr);
1695738dd194Shyw 
169625cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
169725cf1a30Sjl 	bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t));
169825cf1a30Sjl 	bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t));
169925cf1a30Sjl 
1700ad59b69dSbm 
170125cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
170225cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
170325cf1a30Sjl 
170425cf1a30Sjl 	/* Now read all the registers into flt_stat */
170525cf1a30Sjl 
17060cc8ae86Sav 	for (i = 0; i < 2; i++) {
17070cc8ae86Sav 		MC_LOG("Reading registers of bank %d\n", bank);
17080cc8ae86Sav 		/* patrol registers */
17090cc8ae86Sav 		mc_read_ptrl_reg(mcp, bank, &flt_stat[i]);
171025cf1a30Sjl 
1711738dd194Shyw 		/*
1712738dd194Shyw 		 * In mirror mode, it is possible that only one bank
1713738dd194Shyw 		 * may report the error. We need to check for it to
1714738dd194Shyw 		 * ensure we pick the right addr value for patrol restart.
1715738dd194Shyw 		 * Note that if both banks reported errors, we pick the
1716738dd194Shyw 		 * 2nd one. Both banks should reported the same error address.
1717738dd194Shyw 		 */
1718738dd194Shyw 		if (flt_stat[i].mf_cntl & MAC_CNTL_PTRL_ERRS)
1719738dd194Shyw 			rsaddr->mi_restartaddr = flt_stat[i].mf_flt_maddr;
172025cf1a30Sjl 
17210cc8ae86Sav 		MC_LOG("ptrl registers cntl %x add %x log %x\n",
1722*d8a0cca9Swh 		    flt_stat[i].mf_cntl, flt_stat[i].mf_err_add,
1723*d8a0cca9Swh 		    flt_stat[i].mf_err_log);
172425cf1a30Sjl 
17250cc8ae86Sav 		/* MI registers */
17260cc8ae86Sav 		mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]);
172725cf1a30Sjl 
17280cc8ae86Sav 		MC_LOG("MI registers cntl %x add %x log %x\n",
1729*d8a0cca9Swh 		    mi_flt_stat[i].mf_cntl, mi_flt_stat[i].mf_err_add,
1730*d8a0cca9Swh 		    mi_flt_stat[i].mf_err_log);
173125cf1a30Sjl 
17320cc8ae86Sav 		bank = bank^1;
17330cc8ae86Sav 	}
173425cf1a30Sjl 
173525cf1a30Sjl 	/* clear errors once we read all the registers */
1736*d8a0cca9Swh 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
173725cf1a30Sjl 
17380cc8ae86Sav 	MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
17390cc8ae86Sav 
17400cc8ae86Sav 	/* Process MI errors first */
174125cf1a30Sjl 
17420cc8ae86Sav 	/* if not error mode, cntl1 is 0 */
17430cc8ae86Sav 	if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
1744*d8a0cca9Swh 	    (mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
17450cc8ae86Sav 		mi_flt_stat[0].mf_cntl = 0;
17460cc8ae86Sav 
17470cc8ae86Sav 	if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
1748*d8a0cca9Swh 	    (mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
17490cc8ae86Sav 		mi_flt_stat[1].mf_cntl = 0;
175025cf1a30Sjl 
17510cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
17520cc8ae86Sav 	mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]);
17530cc8ae86Sav 
17540cc8ae86Sav 	if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1755*d8a0cca9Swh 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[0].mf_cntl &
1756*d8a0cca9Swh 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
1757*d8a0cca9Swh 	    (flt_stat[0].mf_err_add == mi_flt_stat[0].mf_err_add) &&
1758*d8a0cca9Swh 	    (((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1759*d8a0cca9Swh 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[1].mf_cntl &
1760*d8a0cca9Swh 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
1761*d8a0cca9Swh 	    (flt_stat[1].mf_err_add == mi_flt_stat[1].mf_err_add)) {
17620cc8ae86Sav #ifdef DEBUG
17630cc8ae86Sav 		MC_LOG("discarding PTRL error because "
17640cc8ae86Sav 		    "it is the same as MI\n");
17650cc8ae86Sav #endif
1766738dd194Shyw 		rsaddr->mi_valid = mi_valid;
17670cc8ae86Sav 		return;
17680cc8ae86Sav 	}
176925cf1a30Sjl 	/* if not error mode, cntl1 is 0 */
177025cf1a30Sjl 	if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
1771*d8a0cca9Swh 	    (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
177225cf1a30Sjl 		flt_stat[0].mf_cntl = 0;
177325cf1a30Sjl 
177425cf1a30Sjl 	if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
1775*d8a0cca9Swh 	    (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
177625cf1a30Sjl 		flt_stat[1].mf_cntl = 0;
177725cf1a30Sjl 
177825cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
1779738dd194Shyw 	rsaddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]);
178025cf1a30Sjl }
178125cf1a30Sjl static int
178225cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt,
178325cf1a30Sjl 	mc_flt_stat_t *flt_stat)
178425cf1a30Sjl {
178525cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
178625cf1a30Sjl 	int rv = 0;
178725cf1a30Sjl 
178825cf1a30Sjl 	mc_aflt->mflt_erpt_class = NULL;
178925cf1a30Sjl 	if (IS_UE(flt_stat->mf_cntl, ptrl_error)) {
17901039f409Sav 		MC_LOG("UE detected\n");
179125cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_UE;
179225cf1a30Sjl 		mc_aflt->mflt_erpt_class = MC_OPL_UE;
179325cf1a30Sjl 		mc_aflt->mflt_pr = PR_UE;
179425cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
179525cf1a30Sjl 		rv = 1;
179625cf1a30Sjl 	} else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) {
17971039f409Sav 		MC_LOG("CE detected\n");
179825cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
179925cf1a30Sjl 
18001039f409Sav 		/* Error type can change after scrubbing */
180125cf1a30Sjl 		mc_scrub_ce(mcp, bank, flt_stat, ptrl_error);
180225cf1a30Sjl 
180325cf1a30Sjl 		if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
180425cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CE;
180525cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
180625cf1a30Sjl 		} else if (flt_stat->mf_type == FLT_TYPE_UE) {
180725cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_UE;
180825cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
180925cf1a30Sjl 		}
181025cf1a30Sjl 		rv = 1;
181125cf1a30Sjl 	}
1812*d8a0cca9Swh 	MC_LOG("mc_process_error: fault type %x erpt %s\n", flt_stat->mf_type,
1813*d8a0cca9Swh 	    mc_aflt->mflt_erpt_class);
181425cf1a30Sjl 	if (mc_aflt->mflt_erpt_class) {
181525cf1a30Sjl 		mc_aflt->mflt_stat[0] = flt_stat;
181625cf1a30Sjl 		mc_aflt->mflt_nflts = 1;
181725cf1a30Sjl 		mc_err_drain(mc_aflt);
181825cf1a30Sjl 	}
181925cf1a30Sjl 	return (rv);
182025cf1a30Sjl }
182125cf1a30Sjl 
182225cf1a30Sjl static void
1823738dd194Shyw mc_error_handler(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr)
182425cf1a30Sjl {
182525cf1a30Sjl 	mc_aflt_t mc_aflt;
182625cf1a30Sjl 	mc_flt_stat_t flt_stat, mi_flt_stat;
18270cc8ae86Sav 	int mi_valid;
182825cf1a30Sjl 
182925cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
183025cf1a30Sjl 	bzero(&flt_stat, sizeof (mc_flt_stat_t));
183125cf1a30Sjl 	bzero(&mi_flt_stat, sizeof (mc_flt_stat_t));
183225cf1a30Sjl 
183325cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
183425cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
183525cf1a30Sjl 
183625cf1a30Sjl 	/* patrol registers */
183725cf1a30Sjl 	mc_read_ptrl_reg(mcp, bank, &flt_stat);
183825cf1a30Sjl 
1839738dd194Shyw 	ASSERT(rsaddr);
1840738dd194Shyw 	rsaddr->mi_restartaddr = flt_stat.mf_flt_maddr;
184125cf1a30Sjl 
1842*d8a0cca9Swh 	MC_LOG("ptrl registers cntl %x add %x log %x\n", flt_stat.mf_cntl,
1843*d8a0cca9Swh 	    flt_stat.mf_err_add, flt_stat.mf_err_log);
184425cf1a30Sjl 
184525cf1a30Sjl 	/* MI registers */
184625cf1a30Sjl 	mc_read_mi_reg(mcp, bank, &mi_flt_stat);
184725cf1a30Sjl 
18480cc8ae86Sav 
1849*d8a0cca9Swh 	MC_LOG("MI registers cntl %x add %x log %x\n", mi_flt_stat.mf_cntl,
1850*d8a0cca9Swh 	    mi_flt_stat.mf_err_add, mi_flt_stat.mf_err_log);
185125cf1a30Sjl 
185225cf1a30Sjl 	/* clear errors once we read all the registers */
185325cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
185425cf1a30Sjl 
18550cc8ae86Sav 	mc_aflt.mflt_is_ptrl = 0;
18560cc8ae86Sav 	if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) &&
1857*d8a0cca9Swh 	    ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
1858*d8a0cca9Swh 	    ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
18590cc8ae86Sav 		mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat);
18600cc8ae86Sav 	}
18610cc8ae86Sav 
18620cc8ae86Sav 	if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >>
1863*d8a0cca9Swh 	    MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat.mf_cntl &
1864*d8a0cca9Swh 	    MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
1865*d8a0cca9Swh 	    (flt_stat.mf_err_add == mi_flt_stat.mf_err_add)) {
18660cc8ae86Sav #ifdef DEBUG
18670cc8ae86Sav 		MC_LOG("discarding PTRL error because "
18680cc8ae86Sav 		    "it is the same as MI\n");
18690cc8ae86Sav #endif
1870738dd194Shyw 		rsaddr->mi_valid = mi_valid;
18710cc8ae86Sav 		return;
18720cc8ae86Sav 	}
18730cc8ae86Sav 
187425cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
187525cf1a30Sjl 	if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) &&
1876*d8a0cca9Swh 	    ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
1877*d8a0cca9Swh 	    ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
1878*d8a0cca9Swh 		rsaddr->mi_valid = mc_process_error(mcp, bank, &mc_aflt,
1879*d8a0cca9Swh 		    &flt_stat);
188025cf1a30Sjl 	}
188125cf1a30Sjl }
188225cf1a30Sjl /*
188325cf1a30Sjl  *	memory patrol error handling algorithm:
188425cf1a30Sjl  *	timeout() is used to do periodic polling
188525cf1a30Sjl  *	This is the flow chart.
188625cf1a30Sjl  *	timeout ->
188725cf1a30Sjl  *	mc_check_errors()
188825cf1a30Sjl  *	    if memory bank is installed, read the status register
188925cf1a30Sjl  *	    if any error bit is set,
189025cf1a30Sjl  *	    -> mc_error_handler()
18911039f409Sav  *		-> read all error registers
189225cf1a30Sjl  *	        -> mc_process_error()
189325cf1a30Sjl  *	            determine error type
189425cf1a30Sjl  *	            rewrite to clear error or scrub to determine CE type
189525cf1a30Sjl  *	            inform SCF on permanent CE
189625cf1a30Sjl  *	        -> mc_err_drain
189725cf1a30Sjl  *	            page offline processing
189825cf1a30Sjl  *	            -> mc_ereport_post()
189925cf1a30Sjl  */
190025cf1a30Sjl 
190125cf1a30Sjl static void
190225cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp)
190325cf1a30Sjl {
1904738dd194Shyw 	mc_rsaddr_info_t rsaddr_info;
190525cf1a30Sjl 	int i, error_count = 0;
190625cf1a30Sjl 	uint32_t stat, cntl;
19070cc8ae86Sav 	int running;
1908cfb9e062Shyw 	int wrapped;
190937afe445Shyw 	int ebk;
191025cf1a30Sjl 
191125cf1a30Sjl 	/*
191225cf1a30Sjl 	 * scan errors.
191325cf1a30Sjl 	 */
19140cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS)
19150cc8ae86Sav 		return;
19160cc8ae86Sav 
191725cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
191825cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
191925cf1a30Sjl 			stat = ldphysio(MAC_PTRL_STAT(mcp, i));
192025cf1a30Sjl 			cntl = ldphysio(MAC_PTRL_CNTL(mcp, i));
19210cc8ae86Sav 			running = cntl & MAC_CNTL_PTRL_START;
1922cfb9e062Shyw 			wrapped = cntl & MAC_CNTL_PTRL_ADD_MAX;
19230cc8ae86Sav 
192437afe445Shyw 			/* Compute the effective bank idx */
192537afe445Shyw 			ebk = (IS_MIRROR(mcp, i)) ? MIRROR_IDX(i) : i;
192637afe445Shyw 
1927cfb9e062Shyw 			if (mc_debug_show_all || stat) {
1928cfb9e062Shyw 				MC_LOG("/LSB%d/B%d stat %x cntl %x\n",
1929*d8a0cca9Swh 				    mcp->mc_board_num, i, stat, cntl);
1930cfb9e062Shyw 			}
1931cfb9e062Shyw 
1932cfb9e062Shyw 			/*
1933cfb9e062Shyw 			 * Update stats and reset flag if the HW patrol
1934cfb9e062Shyw 			 * wrapped around in its scan.
1935cfb9e062Shyw 			 */
1936cfb9e062Shyw 			if (wrapped) {
193725cf1a30Sjl 				MAC_CLEAR_MAX(mcp, i);
193837afe445Shyw 				mcp->mc_period[ebk]++;
193937afe445Shyw 				if (IS_MIRROR(mcp, i))
1940*d8a0cca9Swh 					MC_LOG("mirror mc period %ld on "
1941*d8a0cca9Swh 					    "/LSB%d/B%d\n", mcp->mc_period[ebk],
1942*d8a0cca9Swh 					    mcp->mc_board_num, i);
194337afe445Shyw 				else {
1944*d8a0cca9Swh 					MC_LOG("mc period %ld on "
1945*d8a0cca9Swh 					    "/LSB%d/B%d\n", mcp->mc_period[ebk],
1946*d8a0cca9Swh 					    mcp->mc_board_num, i);
194737afe445Shyw 				}
194825cf1a30Sjl 			}
1949cfb9e062Shyw 
1950cfb9e062Shyw 			if (running) {
1951cfb9e062Shyw 				/*
1952cfb9e062Shyw 				 * Mac patrol HW is still running.
1953cfb9e062Shyw 				 * Normally when an error is detected,
1954cfb9e062Shyw 				 * the HW patrol will stop so that we
1955cfb9e062Shyw 				 * can collect error data for reporting.
1956cfb9e062Shyw 				 * Certain errors (MI errors) detected may not
1957cfb9e062Shyw 				 * cause the HW patrol to stop which is a
1958cfb9e062Shyw 				 * problem since we cannot read error data while
1959cfb9e062Shyw 				 * the HW patrol is running. SW is not allowed
1960cfb9e062Shyw 				 * to stop the HW patrol while it is running
1961cfb9e062Shyw 				 * as it may cause HW inconsistency. This is
1962cfb9e062Shyw 				 * described in a HW errata.
1963cfb9e062Shyw 				 * In situations where we detected errors
1964cfb9e062Shyw 				 * that may not cause the HW patrol to stop.
1965cfb9e062Shyw 				 * We speed up the HW patrol scanning in
1966cfb9e062Shyw 				 * the hope that it will find the 'real' PTRL
1967cfb9e062Shyw 				 * errors associated with the previous errors
1968cfb9e062Shyw 				 * causing the HW to finally stop so that we
1969cfb9e062Shyw 				 * can do the reporting.
1970cfb9e062Shyw 				 */
1971cfb9e062Shyw 				/*
1972cfb9e062Shyw 				 * Check to see if we did speed up
1973cfb9e062Shyw 				 * the HW patrol due to previous errors
1974cfb9e062Shyw 				 * detected that did not cause the patrol
1975cfb9e062Shyw 				 * to stop. We only do it if HW patrol scan
1976cfb9e062Shyw 				 * wrapped (counted as completing a 'period').
1977cfb9e062Shyw 				 */
197837afe445Shyw 				if (mcp->mc_speedup_period[ebk] > 0) {
1979*d8a0cca9Swh 					if (wrapped &&
1980*d8a0cca9Swh 					    (--mcp->mc_speedup_period[ebk] ==
1981*d8a0cca9Swh 					    0)) {
1982*d8a0cca9Swh 						/*
1983*d8a0cca9Swh 						 * We did try to speed up.
1984*d8a0cca9Swh 						 * The speed up period has
1985*d8a0cca9Swh 						 * expired and the HW patrol
1986*d8a0cca9Swh 						 * is still running.  The
1987*d8a0cca9Swh 						 * errors must be intermittent.
1988*d8a0cca9Swh 						 * We have no choice but to
1989*d8a0cca9Swh 						 * ignore them, reset the scan
1990*d8a0cca9Swh 						 * speed to normal and clear
1991*d8a0cca9Swh 						 * the MI error bits. For
1992*d8a0cca9Swh 						 * mirror mode, we need to
1993*d8a0cca9Swh 						 * clear errors on both banks.
1994*d8a0cca9Swh 						 */
1995*d8a0cca9Swh 						MC_LOG("Clearing MI errors\n");
1996*d8a0cca9Swh 						MAC_CLEAR_ERRS(mcp, i,
1997*d8a0cca9Swh 						    MAC_CNTL_MI_ERRS);
1998*d8a0cca9Swh 
1999*d8a0cca9Swh 						if (IS_MIRROR(mcp, i)) {
2000*d8a0cca9Swh 							MC_LOG("Clearing "
2001*d8a0cca9Swh 							    "Mirror MI errs\n");
2002*d8a0cca9Swh 							MAC_CLEAR_ERRS(mcp,
2003*d8a0cca9Swh 							    i^1,
2004*d8a0cca9Swh 							    MAC_CNTL_MI_ERRS);
2005*d8a0cca9Swh 						}
200637afe445Shyw 					}
2007cfb9e062Shyw 				} else if (stat & MAC_STAT_MI_ERRS) {
2008cfb9e062Shyw 					/*
2009cfb9e062Shyw 					 * MI errors detected but we cannot
2010cfb9e062Shyw 					 * report them since the HW patrol
2011cfb9e062Shyw 					 * is still running.
2012cfb9e062Shyw 					 * We will attempt to speed up the
2013cfb9e062Shyw 					 * scanning and hopefully the HW
2014cfb9e062Shyw 					 * can detect PRTL errors at the same
2015cfb9e062Shyw 					 * location that cause the HW patrol
2016cfb9e062Shyw 					 * to stop.
2017cfb9e062Shyw 					 */
201837afe445Shyw 					mcp->mc_speedup_period[ebk] = 2;
20190cc8ae86Sav 					MAC_CMD(mcp, i, 0);
20200cc8ae86Sav 				}
2021cfb9e062Shyw 			} else if (stat & (MAC_STAT_PTRL_ERRS |
2022cfb9e062Shyw 			    MAC_STAT_MI_ERRS)) {
2023cfb9e062Shyw 				/*
2024cfb9e062Shyw 				 * HW Patrol has stopped and we found errors.
2025cfb9e062Shyw 				 * Proceed to collect and report error info.
2026cfb9e062Shyw 				 */
202737afe445Shyw 				mcp->mc_speedup_period[ebk] = 0;
2028738dd194Shyw 				rsaddr_info.mi_valid = 0;
2029738dd194Shyw 				rsaddr_info.mi_injectrestart = 0;
2030738dd194Shyw 				if (IS_MIRROR(mcp, i)) {
2031*d8a0cca9Swh 					mc_error_handler_mir(mcp, i,
2032*d8a0cca9Swh 					    &rsaddr_info);
2033738dd194Shyw 				} else {
2034*d8a0cca9Swh 					mc_error_handler(mcp, i, &rsaddr_info);
2035738dd194Shyw 				}
2036cfb9e062Shyw 
2037cfb9e062Shyw 				error_count++;
2038738dd194Shyw 				restart_patrol(mcp, i, &rsaddr_info);
203925cf1a30Sjl 			} else {
2040cfb9e062Shyw 				/*
2041cfb9e062Shyw 				 * HW patrol scan has apparently stopped
2042cfb9e062Shyw 				 * but no errors detected/flagged.
2043cfb9e062Shyw 				 * Restart the HW patrol just to be sure.
204437afe445Shyw 				 * In mirror mode, the odd bank might have
204537afe445Shyw 				 * reported errors that caused the patrol to
204637afe445Shyw 				 * stop. We'll defer the restart to the odd
204737afe445Shyw 				 * bank in this case.
2048cfb9e062Shyw 				 */
204937afe445Shyw 				if (!IS_MIRROR(mcp, i) || (i & 0x1))
205037afe445Shyw 					restart_patrol(mcp, i, NULL);
205125cf1a30Sjl 			}
205225cf1a30Sjl 		}
205325cf1a30Sjl 	}
205425cf1a30Sjl 	if (error_count > 0)
205525cf1a30Sjl 		mcp->mc_last_error += error_count;
205625cf1a30Sjl 	else
205725cf1a30Sjl 		mcp->mc_last_error = 0;
205825cf1a30Sjl }
205925cf1a30Sjl 
20600cc8ae86Sav /*
20610cc8ae86Sav  * mc_polling -- Check errors for only one instance,
20620cc8ae86Sav  * but process errors for all instances to make sure we drain the errors
20630cc8ae86Sav  * faster than they can be accumulated.
20640cc8ae86Sav  *
20650cc8ae86Sav  * Polling on each board should be done only once per each
20660cc8ae86Sav  * mc_patrol_interval_sec.  This is equivalent to setting mc_tick_left
20670cc8ae86Sav  * to OPL_MAX_BOARDS and decrement by 1 on each timeout.
20680cc8ae86Sav  * Once mc_tick_left becomes negative, the board becomes a candidate
20690cc8ae86Sav  * for polling because it has waited for at least
20700cc8ae86Sav  * mc_patrol_interval_sec's long.    If mc_timeout_period is calculated
20711039f409Sav  * differently, this has to be updated accordingly.
20720cc8ae86Sav  */
207325cf1a30Sjl 
207425cf1a30Sjl static void
20750cc8ae86Sav mc_polling(void)
207625cf1a30Sjl {
20770cc8ae86Sav 	int i, scan_error;
20780cc8ae86Sav 	mc_opl_t *mcp;
207925cf1a30Sjl 
208025cf1a30Sjl 
20810cc8ae86Sav 	scan_error = 1;
20820cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
20830cc8ae86Sav 		mutex_enter(&mcmutex);
20840cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL) {
20850cc8ae86Sav 			mutex_exit(&mcmutex);
20860cc8ae86Sav 			continue;
20870cc8ae86Sav 		}
20880cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
20890cc8ae86Sav 		mutex_exit(&mcmutex);
2090738dd194Shyw 		if (!(mcp->mc_status & MC_POLL_RUNNING)) {
2091738dd194Shyw 			mutex_exit(&mcp->mc_lock);
2092738dd194Shyw 			continue;
2093738dd194Shyw 		}
20940cc8ae86Sav 		if (scan_error && mcp->mc_tick_left <= 0) {
20950cc8ae86Sav 			mc_check_errors_func((void *)mcp);
20960cc8ae86Sav 			mcp->mc_tick_left = OPL_MAX_BOARDS;
20970cc8ae86Sav 			scan_error = 0;
20980cc8ae86Sav 		} else {
20990cc8ae86Sav 			mcp->mc_tick_left--;
21000cc8ae86Sav 		}
21010cc8ae86Sav 		mc_process_scf_log(mcp);
21020cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
210325cf1a30Sjl 	}
210425cf1a30Sjl }
210525cf1a30Sjl 
210625cf1a30Sjl static void
210725cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr)
210825cf1a30Sjl {
210925cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
211025cf1a30Sjl 	maddr->ma_bank = bank;
211125cf1a30Sjl 	maddr->ma_dimm_addr = 0;
211225cf1a30Sjl }
211325cf1a30Sjl 
211425cf1a30Sjl typedef struct mc_mem_range {
211525cf1a30Sjl 	uint64_t	addr;
211625cf1a30Sjl 	uint64_t	size;
211725cf1a30Sjl } mc_mem_range_t;
211825cf1a30Sjl 
211925cf1a30Sjl static int
212025cf1a30Sjl get_base_address(mc_opl_t *mcp)
212125cf1a30Sjl {
212225cf1a30Sjl 	mc_mem_range_t *mem_range;
212325cf1a30Sjl 	int len;
212425cf1a30Sjl 
212525cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
2126*d8a0cca9Swh 	    "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) {
212725cf1a30Sjl 		return (DDI_FAILURE);
212825cf1a30Sjl 	}
212925cf1a30Sjl 
213025cf1a30Sjl 	mcp->mc_start_address = mem_range->addr;
213125cf1a30Sjl 	mcp->mc_size = mem_range->size;
213225cf1a30Sjl 
213325cf1a30Sjl 	kmem_free(mem_range, len);
213425cf1a30Sjl 	return (DDI_SUCCESS);
213525cf1a30Sjl }
213625cf1a30Sjl 
213725cf1a30Sjl struct mc_addr_spec {
213825cf1a30Sjl 	uint32_t bank;
213925cf1a30Sjl 	uint32_t phys_hi;
214025cf1a30Sjl 	uint32_t phys_lo;
214125cf1a30Sjl };
214225cf1a30Sjl 
214325cf1a30Sjl #define	REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo)
214425cf1a30Sjl 
214525cf1a30Sjl static char *mc_tbl_name[] = {
214625cf1a30Sjl 	"cs0-mc-pa-trans-table",
214725cf1a30Sjl 	"cs1-mc-pa-trans-table"
214825cf1a30Sjl };
214925cf1a30Sjl 
2150738dd194Shyw /*
2151738dd194Shyw  * This routine performs a rangecheck for a given PA
2152738dd194Shyw  * to see if it belongs to the memory range for this board.
2153738dd194Shyw  * Return 1 if it is valid (within the range) and 0 otherwise
2154738dd194Shyw  */
215525cf1a30Sjl static int
2156738dd194Shyw mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa)
215725cf1a30Sjl {
2158*d8a0cca9Swh 	if ((pa < mcp->mc_start_address) || (mcp->mc_start_address +
2159*d8a0cca9Swh 	    mcp->mc_size <= pa))
2160738dd194Shyw 		return (0);
2161738dd194Shyw 	else
2162738dd194Shyw 		return (1);
216325cf1a30Sjl }
216425cf1a30Sjl 
216525cf1a30Sjl static void
216625cf1a30Sjl mc_memlist_delete(struct memlist *mlist)
216725cf1a30Sjl {
216825cf1a30Sjl 	struct memlist *ml;
216925cf1a30Sjl 
217025cf1a30Sjl 	for (ml = mlist; ml; ml = mlist) {
217125cf1a30Sjl 		mlist = ml->next;
217225cf1a30Sjl 		kmem_free(ml, sizeof (struct memlist));
217325cf1a30Sjl 	}
217425cf1a30Sjl }
217525cf1a30Sjl 
217625cf1a30Sjl static struct memlist *
217725cf1a30Sjl mc_memlist_dup(struct memlist *mlist)
217825cf1a30Sjl {
217925cf1a30Sjl 	struct memlist *hl = NULL, *tl, **mlp;
218025cf1a30Sjl 
218125cf1a30Sjl 	if (mlist == NULL)
218225cf1a30Sjl 		return (NULL);
218325cf1a30Sjl 
218425cf1a30Sjl 	mlp = &hl;
218525cf1a30Sjl 	tl = *mlp;
218625cf1a30Sjl 	for (; mlist; mlist = mlist->next) {
218725cf1a30Sjl 		*mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP);
218825cf1a30Sjl 		(*mlp)->address = mlist->address;
218925cf1a30Sjl 		(*mlp)->size = mlist->size;
219025cf1a30Sjl 		(*mlp)->prev = tl;
219125cf1a30Sjl 		tl = *mlp;
219225cf1a30Sjl 		mlp = &((*mlp)->next);
219325cf1a30Sjl 	}
219425cf1a30Sjl 	*mlp = NULL;
219525cf1a30Sjl 
219625cf1a30Sjl 	return (hl);
219725cf1a30Sjl }
219825cf1a30Sjl 
219925cf1a30Sjl 
220025cf1a30Sjl static struct memlist *
220125cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len)
220225cf1a30Sjl {
220325cf1a30Sjl 	uint64_t	end;
220425cf1a30Sjl 	struct memlist	*ml, *tl, *nlp;
220525cf1a30Sjl 
220625cf1a30Sjl 	if (mlist == NULL)
220725cf1a30Sjl 		return (NULL);
220825cf1a30Sjl 
220925cf1a30Sjl 	end = base + len;
221025cf1a30Sjl 	if ((end <= mlist->address) || (base == end))
221125cf1a30Sjl 		return (mlist);
221225cf1a30Sjl 
221325cf1a30Sjl 	for (tl = ml = mlist; ml; tl = ml, ml = nlp) {
221425cf1a30Sjl 		uint64_t	mend;
221525cf1a30Sjl 
221625cf1a30Sjl 		nlp = ml->next;
221725cf1a30Sjl 
221825cf1a30Sjl 		if (end <= ml->address)
221925cf1a30Sjl 			break;
222025cf1a30Sjl 
222125cf1a30Sjl 		mend = ml->address + ml->size;
222225cf1a30Sjl 		if (base < mend) {
222325cf1a30Sjl 			if (base <= ml->address) {
222425cf1a30Sjl 				ml->address = end;
222525cf1a30Sjl 				if (end >= mend)
222625cf1a30Sjl 					ml->size = 0ull;
222725cf1a30Sjl 				else
222825cf1a30Sjl 					ml->size = mend - ml->address;
222925cf1a30Sjl 			} else {
223025cf1a30Sjl 				ml->size = base - ml->address;
223125cf1a30Sjl 				if (end < mend) {
223225cf1a30Sjl 					struct memlist	*nl;
223325cf1a30Sjl 					/*
223425cf1a30Sjl 					 * splitting an memlist entry.
223525cf1a30Sjl 					 */
223625cf1a30Sjl 					nl = kmem_alloc(sizeof (struct memlist),
2237*d8a0cca9Swh 					    KM_SLEEP);
223825cf1a30Sjl 					nl->address = end;
223925cf1a30Sjl 					nl->size = mend - nl->address;
224025cf1a30Sjl 					if ((nl->next = nlp) != NULL)
224125cf1a30Sjl 						nlp->prev = nl;
224225cf1a30Sjl 					nl->prev = ml;
224325cf1a30Sjl 					ml->next = nl;
224425cf1a30Sjl 					nlp = nl;
224525cf1a30Sjl 				}
224625cf1a30Sjl 			}
224725cf1a30Sjl 			if (ml->size == 0ull) {
224825cf1a30Sjl 				if (ml == mlist) {
224925cf1a30Sjl 					if ((mlist = nlp) != NULL)
225025cf1a30Sjl 						nlp->prev = NULL;
225125cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
225225cf1a30Sjl 					if (mlist == NULL)
225325cf1a30Sjl 						break;
225425cf1a30Sjl 					ml = nlp;
225525cf1a30Sjl 				} else {
225625cf1a30Sjl 					if ((tl->next = nlp) != NULL)
225725cf1a30Sjl 						nlp->prev = tl;
225825cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
225925cf1a30Sjl 					ml = tl;
226025cf1a30Sjl 				}
226125cf1a30Sjl 			}
226225cf1a30Sjl 		}
226325cf1a30Sjl 	}
226425cf1a30Sjl 
226525cf1a30Sjl 	return (mlist);
226625cf1a30Sjl }
226725cf1a30Sjl 
226825cf1a30Sjl static void
226925cf1a30Sjl mc_get_mlist(mc_opl_t *mcp)
227025cf1a30Sjl {
227125cf1a30Sjl 	struct memlist *mlist;
227225cf1a30Sjl 
227325cf1a30Sjl 	memlist_read_lock();
227425cf1a30Sjl 	mlist = mc_memlist_dup(phys_install);
227525cf1a30Sjl 	memlist_read_unlock();
227625cf1a30Sjl 
227725cf1a30Sjl 	if (mlist) {
227825cf1a30Sjl 		mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address);
227925cf1a30Sjl 	}
228025cf1a30Sjl 
228125cf1a30Sjl 	if (mlist) {
228225cf1a30Sjl 		uint64_t startpa, endpa;
228325cf1a30Sjl 
228425cf1a30Sjl 		startpa = mcp->mc_start_address + mcp->mc_size;
228525cf1a30Sjl 		endpa = ptob(physmax + 1);
228625cf1a30Sjl 		if (endpa > startpa) {
2287*d8a0cca9Swh 			mlist = mc_memlist_del_span(mlist, startpa,
2288*d8a0cca9Swh 			    endpa - startpa);
228925cf1a30Sjl 		}
229025cf1a30Sjl 	}
229125cf1a30Sjl 
229225cf1a30Sjl 	if (mlist) {
229325cf1a30Sjl 		mcp->mlist = mlist;
229425cf1a30Sjl 	}
229525cf1a30Sjl }
229625cf1a30Sjl 
229725cf1a30Sjl int
229825cf1a30Sjl mc_board_add(mc_opl_t *mcp)
229925cf1a30Sjl {
230025cf1a30Sjl 	struct mc_addr_spec *macaddr;
23010cc8ae86Sav 	cs_status_t *cs_status;
23020cc8ae86Sav 	int len, len1, i, bk, cc;
2303738dd194Shyw 	mc_rsaddr_info_t rsaddr;
230425cf1a30Sjl 	uint32_t mirr;
23050cc8ae86Sav 	int nbanks = 0;
23060cc8ae86Sav 	uint64_t nbytes = 0;
2307*d8a0cca9Swh 	int mirror_mode = 0;
2308*d8a0cca9Swh 	int ret;
230925cf1a30Sjl 
231025cf1a30Sjl 	/*
231125cf1a30Sjl 	 * Get configurations from "pseudo-mc" node which includes:
231225cf1a30Sjl 	 * board# : LSB number
231325cf1a30Sjl 	 * mac-addr : physical base address of MAC registers
231425cf1a30Sjl 	 * csX-mac-pa-trans-table: translation table from DIMM address
231525cf1a30Sjl 	 *			to physical address or vice versa.
231625cf1a30Sjl 	 */
231725cf1a30Sjl 	mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip,
2318*d8a0cca9Swh 	    DDI_PROP_DONTPASS, "board#", -1);
231925cf1a30Sjl 
23200cc8ae86Sav 	if (mcp->mc_board_num == -1) {
23210cc8ae86Sav 		return (DDI_FAILURE);
23220cc8ae86Sav 	}
23230cc8ae86Sav 
232425cf1a30Sjl 	/*
232525cf1a30Sjl 	 * Get start address in this CAB. It can be gotten from
232625cf1a30Sjl 	 * "sb-mem-ranges" property.
232725cf1a30Sjl 	 */
232825cf1a30Sjl 
232925cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
233025cf1a30Sjl 		return (DDI_FAILURE);
233125cf1a30Sjl 	}
233225cf1a30Sjl 	/* get mac-pa trans tables */
233325cf1a30Sjl 	for (i = 0; i < MC_TT_CS; i++) {
233425cf1a30Sjl 		len = MC_TT_ENTRIES;
233525cf1a30Sjl 		cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip,
2336*d8a0cca9Swh 		    DDI_PROP_DONTPASS, mc_tbl_name[i],
2337*d8a0cca9Swh 		    (caddr_t)mcp->mc_trans_table[i], &len);
233825cf1a30Sjl 
233925cf1a30Sjl 		if (cc != DDI_SUCCESS) {
234025cf1a30Sjl 			bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES);
234125cf1a30Sjl 		}
234225cf1a30Sjl 	}
234325cf1a30Sjl 	mcp->mlist = NULL;
234425cf1a30Sjl 
234525cf1a30Sjl 	mc_get_mlist(mcp);
234625cf1a30Sjl 
234725cf1a30Sjl 	/* initialize bank informations */
234825cf1a30Sjl 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
2349*d8a0cca9Swh 	    "mc-addr", (caddr_t)&macaddr, &len);
235025cf1a30Sjl 	if (cc != DDI_SUCCESS) {
235125cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc);
235225cf1a30Sjl 		return (DDI_FAILURE);
235325cf1a30Sjl 	}
235425cf1a30Sjl 
23550cc8ae86Sav 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
2356*d8a0cca9Swh 	    "cs-status", (caddr_t)&cs_status, &len1);
235725cf1a30Sjl 
23580cc8ae86Sav 	if (cc != DDI_SUCCESS) {
23590cc8ae86Sav 		if (len > 0)
23600cc8ae86Sav 			kmem_free(macaddr, len);
23610cc8ae86Sav 		cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc);
23620cc8ae86Sav 		return (DDI_FAILURE);
23630cc8ae86Sav 	}
2364aeb241b2Sav 	/* get the physical board number for a given logical board number */
2365aeb241b2Sav 	mcp->mc_phys_board_num = mc_opl_get_physical_board(mcp->mc_board_num);
2366aeb241b2Sav 
2367aeb241b2Sav 	if (mcp->mc_phys_board_num < 0) {
2368aeb241b2Sav 		if (len > 0)
2369aeb241b2Sav 			kmem_free(macaddr, len);
2370aeb241b2Sav 		cmn_err(CE_WARN, "Unable to obtain the physical board number");
2371aeb241b2Sav 		return (DDI_FAILURE);
2372aeb241b2Sav 	}
237325cf1a30Sjl 
23740cc8ae86Sav 	mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL);
23750cc8ae86Sav 
23760cc8ae86Sav 	for (i = 0; i < len1 / sizeof (cs_status_t); i++) {
23770cc8ae86Sav 		nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) |
2378*d8a0cca9Swh 		    ((uint64_t)cs_status[i].cs_avail_low);
23790cc8ae86Sav 	}
23800cc8ae86Sav 	if (len1 > 0)
23810cc8ae86Sav 		kmem_free(cs_status, len1);
23820cc8ae86Sav 	nbanks = len / sizeof (struct mc_addr_spec);
23830cc8ae86Sav 
23840cc8ae86Sav 	if (nbanks > 0)
23850cc8ae86Sav 		nbytes /= nbanks;
23860cc8ae86Sav 	else {
23870cc8ae86Sav 		/* No need to free macaddr because len must be 0 */
23880cc8ae86Sav 		mcp->mc_status |= MC_MEMORYLESS;
23890cc8ae86Sav 		return (DDI_SUCCESS);
23900cc8ae86Sav 	}
23910cc8ae86Sav 
23920cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
23930cc8ae86Sav 		mcp->mc_scf_retry[i] = 0;
23940cc8ae86Sav 		mcp->mc_period[i] = 0;
23950cc8ae86Sav 		mcp->mc_speedup_period[i] = 0;
23960cc8ae86Sav 	}
23970cc8ae86Sav 
23980cc8ae86Sav 	/*
23990cc8ae86Sav 	 * Get the memory size here. Let it be B (bytes).
24000cc8ae86Sav 	 * Let T be the time in u.s. to scan 64 bytes.
24010cc8ae86Sav 	 * If we want to complete 1 round of scanning in P seconds.
24020cc8ae86Sav 	 *
24030cc8ae86Sav 	 *	B * T * 10^(-6)	= P
24040cc8ae86Sav 	 *	---------------
24050cc8ae86Sav 	 *		64
24060cc8ae86Sav 	 *
24070cc8ae86Sav 	 *	T = P * 64 * 10^6
24080cc8ae86Sav 	 *	    -------------
24090cc8ae86Sav 	 *		B
24100cc8ae86Sav 	 *
24110cc8ae86Sav 	 *	  = P * 64 * 10^6
24120cc8ae86Sav 	 *	    -------------
24130cc8ae86Sav 	 *		B
24140cc8ae86Sav 	 *
24150cc8ae86Sav 	 *	The timing bits are set in PTRL_CNTL[28:26] where
24160cc8ae86Sav 	 *
24170cc8ae86Sav 	 *	0	- 1 m.s
24180cc8ae86Sav 	 *	1	- 512 u.s.
24190cc8ae86Sav 	 *	10	- 256 u.s.
24200cc8ae86Sav 	 *	11	- 128 u.s.
24210cc8ae86Sav 	 *	100	- 64 u.s.
24220cc8ae86Sav 	 *	101	- 32 u.s.
24230cc8ae86Sav 	 *	110	- 0 u.s.
24240cc8ae86Sav 	 *	111	- reserved.
24250cc8ae86Sav 	 *
24260cc8ae86Sav 	 *
24270cc8ae86Sav 	 *	a[0] = 110, a[1] = 101, ... a[6] = 0
24280cc8ae86Sav 	 *
24290cc8ae86Sav 	 *	cs-status property is int x 7
24300cc8ae86Sav 	 *	0 - cs#
24310cc8ae86Sav 	 *	1 - cs-status
24320cc8ae86Sav 	 *	2 - cs-avail.hi
24330cc8ae86Sav 	 *	3 - cs-avail.lo
24340cc8ae86Sav 	 *	4 - dimm-capa.hi
24350cc8ae86Sav 	 *	5 - dimm-capa.lo
24360cc8ae86Sav 	 *	6 - #of dimms
24370cc8ae86Sav 	 */
24380cc8ae86Sav 
24390cc8ae86Sav 	if (nbytes > 0) {
24400cc8ae86Sav 		int i;
24410cc8ae86Sav 		uint64_t ms;
24420cc8ae86Sav 		ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes;
24430cc8ae86Sav 		mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds;
24440cc8ae86Sav 		for (i = 0; i < MC_MAX_SPEEDS - 1; i++) {
24450cc8ae86Sav 			if (ms < mc_scan_speeds[i + 1].mc_period) {
24460cc8ae86Sav 				mcp->mc_speed = mc_scan_speeds[i].mc_speeds;
24470cc8ae86Sav 				break;
24480cc8ae86Sav 			}
24490cc8ae86Sav 		}
24500cc8ae86Sav 	} else
24510cc8ae86Sav 		mcp->mc_speed = 0;
24520cc8ae86Sav 
24530cc8ae86Sav 
24540cc8ae86Sav 	for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) {
24550cc8ae86Sav 		struct mc_bank *bankp;
24560cc8ae86Sav 		uint32_t reg;
24570cc8ae86Sav 
24580cc8ae86Sav 		/*
24590cc8ae86Sav 		 * setup bank
24600cc8ae86Sav 		 */
24610cc8ae86Sav 		bk = macaddr[i].bank;
24620cc8ae86Sav 		bankp = &(mcp->mc_bank[bk]);
24630cc8ae86Sav 		bankp->mcb_status = BANK_INSTALLED;
24640cc8ae86Sav 		bankp->mcb_reg_base = REGS_PA(macaddr, i);
24650cc8ae86Sav 
24660cc8ae86Sav 		reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk));
24670cc8ae86Sav 		bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS);
246825cf1a30Sjl 
246925cf1a30Sjl 		/*
247025cf1a30Sjl 		 * check if mirror mode
247125cf1a30Sjl 		 */
247225cf1a30Sjl 		mirr = LD_MAC_REG(MAC_MIRR(mcp, bk));
247325cf1a30Sjl 
247425cf1a30Sjl 		if (mirr & MAC_MIRR_MIRROR_MODE) {
2475*d8a0cca9Swh 			MC_LOG("Mirror -> /LSB%d/B%d\n", mcp->mc_board_num,
2476*d8a0cca9Swh 			    bk);
247725cf1a30Sjl 			bankp->mcb_status |= BANK_MIRROR_MODE;
2478*d8a0cca9Swh 			mirror_mode = 1;
247925cf1a30Sjl 			/*
248025cf1a30Sjl 			 * The following bit is only used for
248125cf1a30Sjl 			 * error injection.  We should clear it
248225cf1a30Sjl 			 */
248325cf1a30Sjl 			if (mirr & MAC_MIRR_BANK_EXCLUSIVE)
2484*d8a0cca9Swh 				ST_MAC_REG(MAC_MIRR(mcp, bk), 0);
248525cf1a30Sjl 		}
248625cf1a30Sjl 
248725cf1a30Sjl 		/*
248825cf1a30Sjl 		 * restart if not mirror mode or the other bank
248925cf1a30Sjl 		 * of the mirror is not running
249025cf1a30Sjl 		 */
249125cf1a30Sjl 		if (!(mirr & MAC_MIRR_MIRROR_MODE) ||
2492*d8a0cca9Swh 		    !(mcp->mc_bank[bk^1].mcb_status & BANK_PTRL_RUNNING)) {
2493*d8a0cca9Swh 			MC_LOG("Starting up /LSB%d/B%d\n", mcp->mc_board_num,
2494*d8a0cca9Swh 			    bk);
2495738dd194Shyw 			get_ptrl_start_address(mcp, bk, &rsaddr.mi_restartaddr);
2496738dd194Shyw 			rsaddr.mi_valid = 0;
2497738dd194Shyw 			rsaddr.mi_injectrestart = 0;
2498738dd194Shyw 			restart_patrol(mcp, bk, &rsaddr);
249925cf1a30Sjl 		} else {
250025cf1a30Sjl 			MC_LOG("Not starting up /LSB%d/B%d\n",
2501*d8a0cca9Swh 			    mcp->mc_board_num, bk);
250225cf1a30Sjl 		}
250325cf1a30Sjl 		bankp->mcb_status |= BANK_PTRL_RUNNING;
250425cf1a30Sjl 	}
25050cc8ae86Sav 	if (len > 0)
25060cc8ae86Sav 		kmem_free(macaddr, len);
25070cc8ae86Sav 
2508*d8a0cca9Swh 	ret = ndi_prop_update_int(DDI_DEV_T_NONE, mcp->mc_dip, "mirror-mode",
2509*d8a0cca9Swh 	    mirror_mode);
2510*d8a0cca9Swh 	if (ret != DDI_PROP_SUCCESS) {
2511*d8a0cca9Swh 		cmn_err(CE_WARN, "Unable to update mirror-mode property");
2512*d8a0cca9Swh 	}
2513*d8a0cca9Swh 
25140cc8ae86Sav 	mcp->mc_dimm_list = mc_get_dimm_list(mcp);
251525cf1a30Sjl 
251625cf1a30Sjl 	/*
251725cf1a30Sjl 	 * set interval in HZ.
251825cf1a30Sjl 	 */
251925cf1a30Sjl 	mcp->mc_last_error = 0;
252025cf1a30Sjl 
252125cf1a30Sjl 	/* restart memory patrol checking */
252225cf1a30Sjl 	mcp->mc_status |= MC_POLL_RUNNING;
252325cf1a30Sjl 
252425cf1a30Sjl 	return (DDI_SUCCESS);
252525cf1a30Sjl }
252625cf1a30Sjl 
252725cf1a30Sjl int
252825cf1a30Sjl mc_board_del(mc_opl_t *mcp)
252925cf1a30Sjl {
253025cf1a30Sjl 	int i;
253125cf1a30Sjl 	scf_log_t *p;
253225cf1a30Sjl 
253325cf1a30Sjl 	/*
253425cf1a30Sjl 	 * cleanup mac state
253525cf1a30Sjl 	 */
253625cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
25370cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
25380cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
25390cc8ae86Sav 		mutex_destroy(&mcp->mc_lock);
25400cc8ae86Sav 		return (DDI_SUCCESS);
25410cc8ae86Sav 	}
254225cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
254325cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
254425cf1a30Sjl 			mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED;
254525cf1a30Sjl 		}
254625cf1a30Sjl 	}
254725cf1a30Sjl 
254825cf1a30Sjl 	/* stop memory patrol checking */
2549738dd194Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
255025cf1a30Sjl 
255125cf1a30Sjl 	/* just throw away all the scf logs */
25520cc8ae86Sav 	for (i = 0; i < BANKNUM_PER_SB; i++) {
2553*d8a0cca9Swh 		while ((p = mcp->mc_scf_log[i]) != NULL) {
2554*d8a0cca9Swh 			mcp->mc_scf_log[i] = p->sl_next;
2555*d8a0cca9Swh 			mcp->mc_scf_total[i]--;
2556*d8a0cca9Swh 			kmem_free(p, sizeof (scf_log_t));
2557*d8a0cca9Swh 		}
255825cf1a30Sjl 	}
255925cf1a30Sjl 
256025cf1a30Sjl 	if (mcp->mlist)
256125cf1a30Sjl 		mc_memlist_delete(mcp->mlist);
256225cf1a30Sjl 
25630cc8ae86Sav 	if (mcp->mc_dimm_list)
25640cc8ae86Sav 		mc_free_dimm_list(mcp->mc_dimm_list);
25650cc8ae86Sav 
256625cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
256725cf1a30Sjl 
256825cf1a30Sjl 	mutex_destroy(&mcp->mc_lock);
256925cf1a30Sjl 	return (DDI_SUCCESS);
257025cf1a30Sjl }
257125cf1a30Sjl 
257225cf1a30Sjl int
257325cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag)
257425cf1a30Sjl {
257525cf1a30Sjl 	/* stop memory patrol checking */
257625cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
25770cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
25780cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
25790cc8ae86Sav 		return (DDI_SUCCESS);
25800cc8ae86Sav 	}
25810cc8ae86Sav 
2582738dd194Shyw 	mcp->mc_status &= ~MC_POLL_RUNNING;
2583738dd194Shyw 
258425cf1a30Sjl 	mcp->mc_status |= flag;
258525cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
258625cf1a30Sjl 
258725cf1a30Sjl 	return (DDI_SUCCESS);
258825cf1a30Sjl }
258925cf1a30Sjl 
259068ac2337Sjl void
259168ac2337Sjl opl_mc_update_mlist(void)
259268ac2337Sjl {
259368ac2337Sjl 	int i;
259468ac2337Sjl 	mc_opl_t *mcp;
259568ac2337Sjl 
259668ac2337Sjl 	/*
259768ac2337Sjl 	 * memory information is not updated until
259868ac2337Sjl 	 * the post attach/detach stage during DR.
259968ac2337Sjl 	 * This interface is used by dr_mem to inform
260068ac2337Sjl 	 * mc-opl to update the mlist.
260168ac2337Sjl 	 */
260268ac2337Sjl 
260368ac2337Sjl 	mutex_enter(&mcmutex);
260468ac2337Sjl 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
260568ac2337Sjl 		if ((mcp = mc_instances[i]) == NULL)
260668ac2337Sjl 			continue;
260768ac2337Sjl 		mutex_enter(&mcp->mc_lock);
260868ac2337Sjl 		if (mcp->mlist)
260968ac2337Sjl 			mc_memlist_delete(mcp->mlist);
261068ac2337Sjl 		mcp->mlist = NULL;
261168ac2337Sjl 		mc_get_mlist(mcp);
261268ac2337Sjl 		mutex_exit(&mcp->mc_lock);
261368ac2337Sjl 	}
261468ac2337Sjl 	mutex_exit(&mcmutex);
261568ac2337Sjl }
261668ac2337Sjl 
261725cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */
261825cf1a30Sjl 
261925cf1a30Sjl int
262025cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag)
262125cf1a30Sjl {
262225cf1a30Sjl 	int i;
262325cf1a30Sjl 	uint64_t basepa;
262425cf1a30Sjl 
262525cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
26260cc8ae86Sav 	if (mcp->mc_status & MC_MEMORYLESS) {
26270cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
26280cc8ae86Sav 		return (DDI_SUCCESS);
26290cc8ae86Sav 	}
263025cf1a30Sjl 	basepa = mcp->mc_start_address;
263125cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
263225cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
263325cf1a30Sjl 		return (DDI_FAILURE);
263425cf1a30Sjl 	}
263525cf1a30Sjl 
263625cf1a30Sjl 	if (basepa != mcp->mc_start_address) {
263725cf1a30Sjl 		if (mcp->mlist)
263825cf1a30Sjl 			mc_memlist_delete(mcp->mlist);
263925cf1a30Sjl 		mcp->mlist = NULL;
264025cf1a30Sjl 		mc_get_mlist(mcp);
264125cf1a30Sjl 	}
264225cf1a30Sjl 
264325cf1a30Sjl 	mcp->mc_status &= ~flag;
264425cf1a30Sjl 
264525cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
264625cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
264725cf1a30Sjl 		return (DDI_SUCCESS);
264825cf1a30Sjl 	}
264925cf1a30Sjl 
265025cf1a30Sjl 	if (!(mcp->mc_status & MC_POLL_RUNNING)) {
265125cf1a30Sjl 		/* restart memory patrol checking */
265225cf1a30Sjl 		mcp->mc_status |= MC_POLL_RUNNING;
265325cf1a30Sjl 		for (i = 0; i < BANKNUM_PER_SB; i++) {
265425cf1a30Sjl 			if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
265525cf1a30Sjl 				restart_patrol(mcp, i, NULL);
265625cf1a30Sjl 			}
265725cf1a30Sjl 		}
265825cf1a30Sjl 	}
265925cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
266025cf1a30Sjl 
266125cf1a30Sjl 	return (DDI_SUCCESS);
266225cf1a30Sjl }
266325cf1a30Sjl 
266425cf1a30Sjl static mc_opl_t *
266525cf1a30Sjl mc_pa_to_mcp(uint64_t pa)
266625cf1a30Sjl {
26670cc8ae86Sav 	mc_opl_t *mcp;
26680cc8ae86Sav 	int i;
26690cc8ae86Sav 
267025cf1a30Sjl 	ASSERT(MUTEX_HELD(&mcmutex));
26710cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
26720cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
26730cc8ae86Sav 			continue;
267425cf1a30Sjl 		/* if mac patrol is suspended, we cannot rely on it */
26750cc8ae86Sav 		if (!(mcp->mc_status & MC_POLL_RUNNING) ||
2676*d8a0cca9Swh 		    (mcp->mc_status & MC_SOFT_SUSPENDED))
267725cf1a30Sjl 			continue;
2678738dd194Shyw 		if (mc_rangecheck_pa(mcp, pa)) {
26790cc8ae86Sav 			return (mcp);
268025cf1a30Sjl 		}
268125cf1a30Sjl 	}
268225cf1a30Sjl 	return (NULL);
268325cf1a30Sjl }
268425cf1a30Sjl 
268525cf1a30Sjl /*
268625cf1a30Sjl  * Get Physical Board number from Logical one.
268725cf1a30Sjl  */
268825cf1a30Sjl static int
268925cf1a30Sjl mc_opl_get_physical_board(int sb)
269025cf1a30Sjl {
269125cf1a30Sjl 	if (&opl_get_physical_board) {
269225cf1a30Sjl 		return (opl_get_physical_board(sb));
269325cf1a30Sjl 	}
269425cf1a30Sjl 
269525cf1a30Sjl 	cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n");
269625cf1a30Sjl 	return (-1);
269725cf1a30Sjl }
269825cf1a30Sjl 
269925cf1a30Sjl /* ARGSUSED */
270025cf1a30Sjl int
270125cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen,
270225cf1a30Sjl 	int *lenp)
270325cf1a30Sjl {
27040cc8ae86Sav 	int i;
2705aeb241b2Sav 	int j;
270625cf1a30Sjl 	int sb;
27070cc8ae86Sav 	int bank;
2708aeb241b2Sav 	int cs;
27090cc8ae86Sav 	mc_opl_t *mcp;
27100cc8ae86Sav 	char memb_num;
271125cf1a30Sjl 
271225cf1a30Sjl 	mutex_enter(&mcmutex);
271325cf1a30Sjl 
271425cf1a30Sjl 	if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) ||
2715*d8a0cca9Swh 	    (!pa_is_valid(mcp, flt_addr))) {
271625cf1a30Sjl 		mutex_exit(&mcmutex);
271725cf1a30Sjl 		if (snprintf(buf, buflen, "UNKNOWN") >= buflen) {
271825cf1a30Sjl 			return (ENOSPC);
271925cf1a30Sjl 		} else {
272025cf1a30Sjl 			if (lenp)
272125cf1a30Sjl 				*lenp = strlen(buf);
272225cf1a30Sjl 		}
272325cf1a30Sjl 		return (0);
272425cf1a30Sjl 	}
272525cf1a30Sjl 
272625cf1a30Sjl 	bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address);
2727aeb241b2Sav 	sb = mcp->mc_phys_board_num;
2728aeb241b2Sav 	cs = pa_to_cs(mcp, flt_addr - mcp->mc_start_address);
272925cf1a30Sjl 
273025cf1a30Sjl 	if (sb == -1) {
273125cf1a30Sjl 		mutex_exit(&mcmutex);
273225cf1a30Sjl 		return (ENXIO);
273325cf1a30Sjl 	}
273425cf1a30Sjl 
27350cc8ae86Sav 	if (plat_model == MODEL_DC) {
27360cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
2737aeb241b2Sav 		j = (cs == 0) ? i : i + 2;
2738aeb241b2Sav 		snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s",
27390cc8ae86Sav 		    model_names[plat_model].unit_name, sb,
2740aeb241b2Sav 		    mc_dc_dimm_unum_table[j],
2741aeb241b2Sav 		    mc_dc_dimm_unum_table[j + 1]);
274225cf1a30Sjl 	} else {
27430cc8ae86Sav 		i = BD_BK_SLOT_TO_INDEX(sb, bank, 0);
2744aeb241b2Sav 		j = (cs == 0) ? i : i + 2;
27450cc8ae86Sav 		memb_num = mc_ff_dimm_unum_table[i][0];
2746aeb241b2Sav 		snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s",
27470cc8ae86Sav 		    model_names[plat_model].unit_name,
27480cc8ae86Sav 		    model_names[plat_model].mem_name, memb_num,
2749aeb241b2Sav 		    &mc_ff_dimm_unum_table[j][1],
2750aeb241b2Sav 		    &mc_ff_dimm_unum_table[j + 1][1]);
27510cc8ae86Sav 	}
27520cc8ae86Sav 	if (lenp) {
27530cc8ae86Sav 		*lenp = strlen(buf);
275425cf1a30Sjl 	}
275525cf1a30Sjl 	mutex_exit(&mcmutex);
275625cf1a30Sjl 	return (0);
275725cf1a30Sjl }
275825cf1a30Sjl 
275925cf1a30Sjl int
27600cc8ae86Sav opl_mc_suspend(void)
276125cf1a30Sjl {
276225cf1a30Sjl 	mc_opl_t *mcp;
27630cc8ae86Sav 	int i;
276425cf1a30Sjl 
276525cf1a30Sjl 	mutex_enter(&mcmutex);
27660cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
27670cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
27680cc8ae86Sav 			continue;
27690cc8ae86Sav 		mc_suspend(mcp, MC_SOFT_SUSPENDED);
277025cf1a30Sjl 	}
277125cf1a30Sjl 	mutex_exit(&mcmutex);
27720cc8ae86Sav 
277325cf1a30Sjl 	return (0);
277425cf1a30Sjl }
277525cf1a30Sjl 
277625cf1a30Sjl int
27770cc8ae86Sav opl_mc_resume(void)
277825cf1a30Sjl {
277925cf1a30Sjl 	mc_opl_t *mcp;
27800cc8ae86Sav 	int i;
278125cf1a30Sjl 
278225cf1a30Sjl 	mutex_enter(&mcmutex);
27830cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
27840cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
27850cc8ae86Sav 			continue;
27860cc8ae86Sav 		mc_resume(mcp, MC_SOFT_SUSPENDED);
278725cf1a30Sjl 	}
278825cf1a30Sjl 	mutex_exit(&mcmutex);
27890cc8ae86Sav 
279025cf1a30Sjl 	return (0);
279125cf1a30Sjl }
279225cf1a30Sjl static void
279325cf1a30Sjl insert_mcp(mc_opl_t *mcp)
279425cf1a30Sjl {
279525cf1a30Sjl 	mutex_enter(&mcmutex);
27960cc8ae86Sav 	if (mc_instances[mcp->mc_board_num] != NULL) {
27970cc8ae86Sav 		MC_LOG("mc-opl instance for board# %d already exists\n",
2798*d8a0cca9Swh 		    mcp->mc_board_num);
27990cc8ae86Sav 	}
28000cc8ae86Sav 	mc_instances[mcp->mc_board_num] = mcp;
280125cf1a30Sjl 	mutex_exit(&mcmutex);
280225cf1a30Sjl }
280325cf1a30Sjl 
280425cf1a30Sjl static void
280525cf1a30Sjl delete_mcp(mc_opl_t *mcp)
280625cf1a30Sjl {
28070cc8ae86Sav 	mutex_enter(&mcmutex);
28080cc8ae86Sav 	mc_instances[mcp->mc_board_num] = 0;
28090cc8ae86Sav 	mutex_exit(&mcmutex);
281025cf1a30Sjl }
281125cf1a30Sjl 
281225cf1a30Sjl /* Error injection interface */
281325cf1a30Sjl 
2814cfb9e062Shyw static void
2815cfb9e062Shyw mc_lock_va(uint64_t pa, caddr_t new_va)
2816cfb9e062Shyw {
2817cfb9e062Shyw 	tte_t tte;
2818cfb9e062Shyw 
2819738dd194Shyw 	vtag_flushpage(new_va, (uint64_t)ksfmmup);
2820*d8a0cca9Swh 	sfmmu_memtte(&tte, pa >> PAGESHIFT, PROC_DATA|HAT_NOSYNC, TTE8K);
2821cfb9e062Shyw 	tte.tte_intlo |= TTE_LCK_INT;
2822cfb9e062Shyw 	sfmmu_dtlb_ld_kva(new_va, &tte);
2823cfb9e062Shyw }
2824cfb9e062Shyw 
2825cfb9e062Shyw static void
2826cfb9e062Shyw mc_unlock_va(caddr_t va)
2827cfb9e062Shyw {
2828cfb9e062Shyw 	vtag_flushpage(va, (uint64_t)ksfmmup);
2829cfb9e062Shyw }
2830cfb9e062Shyw 
283125cf1a30Sjl /* ARGSUSED */
283225cf1a30Sjl int
283325cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags)
283425cf1a30Sjl {
283525cf1a30Sjl 	mc_opl_t *mcp;
283625cf1a30Sjl 	int bank;
283725cf1a30Sjl 	uint32_t dimm_addr;
283825cf1a30Sjl 	uint32_t cntl;
2839738dd194Shyw 	mc_rsaddr_info_t rsaddr;
284025cf1a30Sjl 	uint32_t data, stat;
284125cf1a30Sjl 	int both_sides = 0;
284225cf1a30Sjl 	uint64_t pa0;
2843cfb9e062Shyw 	int extra_injection_needed = 0;
284425cf1a30Sjl 	extern void cpu_flush_ecache(void);
284525cf1a30Sjl 
284625cf1a30Sjl 	MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags);
284725cf1a30Sjl 
284825cf1a30Sjl 	mutex_enter(&mcmutex);
284925cf1a30Sjl 	if ((mcp = mc_pa_to_mcp(pa)) == NULL) {
285025cf1a30Sjl 		mutex_exit(&mcmutex);
285125cf1a30Sjl 		MC_LOG("mc_inject_error: invalid pa\n");
285225cf1a30Sjl 		return (ENOTSUP);
285325cf1a30Sjl 	}
285425cf1a30Sjl 
285525cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
285625cf1a30Sjl 	mutex_exit(&mcmutex);
285725cf1a30Sjl 
285825cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
285925cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
286025cf1a30Sjl 		MC_LOG("mc-opl has been suspended.  No error injection.\n");
286125cf1a30Sjl 		return (EBUSY);
286225cf1a30Sjl 	}
286325cf1a30Sjl 
286425cf1a30Sjl 	/* convert pa to offset within the board */
286525cf1a30Sjl 	MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address);
286625cf1a30Sjl 
286725cf1a30Sjl 	if (!pa_is_valid(mcp, pa)) {
286825cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
286925cf1a30Sjl 		return (EINVAL);
287025cf1a30Sjl 	}
287125cf1a30Sjl 
287225cf1a30Sjl 	pa0 = pa - mcp->mc_start_address;
287325cf1a30Sjl 
287425cf1a30Sjl 	bank = pa_to_bank(mcp, pa0);
287525cf1a30Sjl 
287625cf1a30Sjl 	if (flags & MC_INJECT_FLAG_OTHER)
287725cf1a30Sjl 		bank = bank ^ 1;
287825cf1a30Sjl 
287925cf1a30Sjl 	if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) {
288025cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
288125cf1a30Sjl 		MC_LOG("Not mirror mode\n");
288225cf1a30Sjl 		return (EINVAL);
288325cf1a30Sjl 	}
288425cf1a30Sjl 
288525cf1a30Sjl 	dimm_addr = pa_to_dimm(mcp, pa0);
288625cf1a30Sjl 
2887*d8a0cca9Swh 	MC_LOG("injecting error to /LSB%d/B%d/%x\n", mcp->mc_board_num, bank,
2888*d8a0cca9Swh 	    dimm_addr);
288925cf1a30Sjl 
289025cf1a30Sjl 
289125cf1a30Sjl 	switch (error_type) {
289225cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
289325cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
289425cf1a30Sjl 	case MC_INJECT_MUE:
289525cf1a30Sjl 		both_sides = 1;
289625cf1a30Sjl 	}
289725cf1a30Sjl 
289825cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESET)
289925cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0);
290025cf1a30Sjl 
290125cf1a30Sjl 	ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK);
290225cf1a30Sjl 
290325cf1a30Sjl 	if (both_sides) {
290425cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0);
2905*d8a0cca9Swh 		ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), dimm_addr &
2906*d8a0cca9Swh 		    MAC_EG_ADD_MASK);
290725cf1a30Sjl 	}
290825cf1a30Sjl 
290925cf1a30Sjl 	switch (error_type) {
291025cf1a30Sjl 	case MC_INJECT_SUE:
2911cfb9e062Shyw 		extra_injection_needed = 1;
2912cfb9e062Shyw 		/*FALLTHROUGH*/
2913cfb9e062Shyw 	case MC_INJECT_UE:
291425cf1a30Sjl 	case MC_INJECT_MUE:
291525cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
2916*d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 |
2917*d8a0cca9Swh 			    MAC_EG_FORCE_READ16 | MAC_EG_RDERR_ONCE;
291825cf1a30Sjl 		} else {
2919*d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR00 |
2920*d8a0cca9Swh 			    MAC_EG_FORCE_DERR16 | MAC_EG_DERR_ONCE;
292125cf1a30Sjl 		}
292225cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
292325cf1a30Sjl 		break;
292425cf1a30Sjl 	case MC_INJECT_INTERMITTENT_CE:
292525cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
292625cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
2927*d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX |MAC_EG_FORCE_READ00 |
2928*d8a0cca9Swh 			    MAC_EG_RDERR_ONCE;
292925cf1a30Sjl 		} else {
2930*d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 |
2931*d8a0cca9Swh 			    MAC_EG_DERR_ONCE;
293225cf1a30Sjl 		}
2933cfb9e062Shyw 		extra_injection_needed = 1;
293425cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
293525cf1a30Sjl 		break;
293625cf1a30Sjl 	case MC_INJECT_PERMANENT_CE:
293725cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
293825cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
2939*d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 |
2940*d8a0cca9Swh 			    MAC_EG_RDERR_ALWAYS;
294125cf1a30Sjl 		} else {
2942*d8a0cca9Swh 			cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 |
2943*d8a0cca9Swh 			    MAC_EG_DERR_ALWAYS;
294425cf1a30Sjl 		}
294525cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
294625cf1a30Sjl 		break;
294725cf1a30Sjl 	case MC_INJECT_CMPE:
294825cf1a30Sjl 		data = 0xabcdefab;
294925cf1a30Sjl 		stphys(pa, data);
295025cf1a30Sjl 		cpu_flush_ecache();
295125cf1a30Sjl 		MC_LOG("CMPE: writing data %x to %lx\n", data, pa);
295225cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE);
295325cf1a30Sjl 		stphys(pa, data ^ 0xffffffff);
2954738dd194Shyw 		membar_sync();
295525cf1a30Sjl 		cpu_flush_ecache();
295625cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), 0);
295725cf1a30Sjl 		MC_LOG("CMPE: write new data %xto %lx\n", data, pa);
295825cf1a30Sjl 		cntl = 0;
295925cf1a30Sjl 		break;
296025cf1a30Sjl 	case MC_INJECT_NOP:
296125cf1a30Sjl 		cntl = 0;
296225cf1a30Sjl 		break;
296325cf1a30Sjl 	default:
296425cf1a30Sjl 		MC_LOG("mc_inject_error: invalid option\n");
296525cf1a30Sjl 		cntl = 0;
296625cf1a30Sjl 	}
296725cf1a30Sjl 
296825cf1a30Sjl 	if (cntl) {
296925cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK);
297025cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
297125cf1a30Sjl 
297225cf1a30Sjl 		if (both_sides) {
297325cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
2974*d8a0cca9Swh 			    MAC_EG_SETUP_MASK);
297525cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
297625cf1a30Sjl 		}
297725cf1a30Sjl 	}
297825cf1a30Sjl 
297925cf1a30Sjl 	/*
298025cf1a30Sjl 	 * For all injection cases except compare error, we
298125cf1a30Sjl 	 * must write to the PA to trigger the error.
298225cf1a30Sjl 	 */
298325cf1a30Sjl 
298425cf1a30Sjl 	if (flags & MC_INJECT_FLAG_ST) {
298525cf1a30Sjl 		data = 0xf0e0d0c0;
298625cf1a30Sjl 		MC_LOG("Writing %x to %lx\n", data, pa);
298725cf1a30Sjl 		stphys(pa, data);
298825cf1a30Sjl 		cpu_flush_ecache();
298925cf1a30Sjl 	}
299025cf1a30Sjl 
299125cf1a30Sjl 
299225cf1a30Sjl 	if (flags & MC_INJECT_FLAG_LD) {
2993cfb9e062Shyw 		if (flags & MC_INJECT_FLAG_PREFETCH) {
2994cfb9e062Shyw 			/*
2995cfb9e062Shyw 			 * Use strong prefetch operation to
2996cfb9e062Shyw 			 * inject MI errors.
2997cfb9e062Shyw 			 */
2998cfb9e062Shyw 			page_t *pp;
2999cfb9e062Shyw 			extern void mc_prefetch(caddr_t);
3000cfb9e062Shyw 
3001cfb9e062Shyw 			MC_LOG("prefetch\n");
3002cfb9e062Shyw 
3003cfb9e062Shyw 			pp = page_numtopp_nolock(pa >> PAGESHIFT);
3004cfb9e062Shyw 			if (pp != NULL) {
3005cfb9e062Shyw 				caddr_t	va, va1;
3006cfb9e062Shyw 
3007cfb9e062Shyw 				va = ppmapin(pp, PROT_READ|PROT_WRITE,
3008*d8a0cca9Swh 				    (caddr_t)-1);
3009cfb9e062Shyw 				kpreempt_disable();
3010cfb9e062Shyw 				mc_lock_va((uint64_t)pa, va);
3011cfb9e062Shyw 				va1 = va + (pa & (PAGESIZE - 1));
3012cfb9e062Shyw 				mc_prefetch(va1);
3013cfb9e062Shyw 				mc_unlock_va(va);
3014cfb9e062Shyw 				kpreempt_enable();
3015cfb9e062Shyw 				ppmapout(va);
3016cfb9e062Shyw 
3017cfb9e062Shyw 				/*
3018cfb9e062Shyw 				 * For MI errors, we need one extra
3019cfb9e062Shyw 				 * injection for HW patrol to stop.
3020cfb9e062Shyw 				 */
3021cfb9e062Shyw 				extra_injection_needed = 1;
302225cf1a30Sjl 			} else {
3023cfb9e062Shyw 				cmn_err(CE_WARN, "Cannot find page structure"
3024*d8a0cca9Swh 				    " for PA %lx\n", pa);
302525cf1a30Sjl 			}
302625cf1a30Sjl 		} else {
302725cf1a30Sjl 			MC_LOG("Reading from %lx\n", pa);
302825cf1a30Sjl 			data = ldphys(pa);
302925cf1a30Sjl 			MC_LOG("data = %x\n", data);
303025cf1a30Sjl 		}
3031cfb9e062Shyw 
3032cfb9e062Shyw 		if (extra_injection_needed) {
3033cfb9e062Shyw 			/*
3034cfb9e062Shyw 			 * These are the injection cases where the
3035cfb9e062Shyw 			 * requested injected errors will not cause the HW
3036cfb9e062Shyw 			 * patrol to stop. For these cases, we need to inject
3037cfb9e062Shyw 			 * an extra 'real' PTRL error to force the
3038cfb9e062Shyw 			 * HW patrol to stop so that we can report the
3039cfb9e062Shyw 			 * errors injected. Note that we cannot read
3040cfb9e062Shyw 			 * and report error status while the HW patrol
3041cfb9e062Shyw 			 * is running.
3042cfb9e062Shyw 			 */
3043cfb9e062Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank),
3044*d8a0cca9Swh 			    cntl & MAC_EG_SETUP_MASK);
3045cfb9e062Shyw 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
3046cfb9e062Shyw 
3047cfb9e062Shyw 			if (both_sides) {
3048*d8a0cca9Swh 				ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
3049*d8a0cca9Swh 				    MAC_EG_SETUP_MASK);
3050*d8a0cca9Swh 				ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
3051cfb9e062Shyw 			}
3052cfb9e062Shyw 			data = 0xf0e0d0c0;
3053cfb9e062Shyw 			MC_LOG("Writing %x to %lx\n", data, pa);
3054cfb9e062Shyw 			stphys(pa, data);
3055cfb9e062Shyw 			cpu_flush_ecache();
3056cfb9e062Shyw 		}
305725cf1a30Sjl 	}
305825cf1a30Sjl 
305925cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESTART) {
306025cf1a30Sjl 		MC_LOG("Restart patrol\n");
3061738dd194Shyw 		rsaddr.mi_restartaddr.ma_bd = mcp->mc_board_num;
3062738dd194Shyw 		rsaddr.mi_restartaddr.ma_bank = bank;
3063738dd194Shyw 		rsaddr.mi_restartaddr.ma_dimm_addr = dimm_addr;
3064738dd194Shyw 		rsaddr.mi_valid = 1;
3065738dd194Shyw 		rsaddr.mi_injectrestart = 1;
3066738dd194Shyw 		restart_patrol(mcp, bank, &rsaddr);
306725cf1a30Sjl 	}
306825cf1a30Sjl 
306925cf1a30Sjl 	if (flags & MC_INJECT_FLAG_POLL) {
30700cc8ae86Sav 		int running;
307137afe445Shyw 		int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank;
307225cf1a30Sjl 
307325cf1a30Sjl 		MC_LOG("Poll patrol error\n");
307425cf1a30Sjl 		stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank));
307525cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
30760cc8ae86Sav 		running = cntl & MAC_CNTL_PTRL_START;
307725cf1a30Sjl 
3078cfb9e062Shyw 		if (!running &&
3079cfb9e062Shyw 		    (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS))) {
3080cfb9e062Shyw 			/*
3081cfb9e062Shyw 			 * HW patrol stopped and we have errors to
3082cfb9e062Shyw 			 * report. Do it.
3083cfb9e062Shyw 			 */
308437afe445Shyw 			mcp->mc_speedup_period[ebank] = 0;
3085738dd194Shyw 			rsaddr.mi_valid = 0;
3086738dd194Shyw 			rsaddr.mi_injectrestart = 0;
3087738dd194Shyw 			if (IS_MIRROR(mcp, bank)) {
3088738dd194Shyw 				mc_error_handler_mir(mcp, bank, &rsaddr);
3089738dd194Shyw 			} else {
3090738dd194Shyw 				mc_error_handler(mcp, bank, &rsaddr);
3091738dd194Shyw 			}
3092cfb9e062Shyw 
3093738dd194Shyw 			restart_patrol(mcp, bank, &rsaddr);
3094cfb9e062Shyw 		} else {
3095cfb9e062Shyw 			/*
3096cfb9e062Shyw 			 * We are expecting to report injected
3097cfb9e062Shyw 			 * errors but the HW patrol is still running.
3098cfb9e062Shyw 			 * Speed up the scanning
3099cfb9e062Shyw 			 */
310037afe445Shyw 			mcp->mc_speedup_period[ebank] = 2;
3101cfb9e062Shyw 			MAC_CMD(mcp, bank, 0);
310225cf1a30Sjl 			restart_patrol(mcp, bank, NULL);
3103cfb9e062Shyw 		}
310425cf1a30Sjl 	}
310525cf1a30Sjl 
310625cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
310725cf1a30Sjl 	return (0);
310825cf1a30Sjl }
3109cfb9e062Shyw 
311025cf1a30Sjl void
311125cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data)
311225cf1a30Sjl {
311325cf1a30Sjl 	MC_LOG("0x%x -> pa(%lx)\n", data, pa);
311425cf1a30Sjl 	stphysio(pa, data);
31150cc8ae86Sav 
31160cc8ae86Sav 	/* force the above write to be processed by mac patrol */
3117cfb9e062Shyw 	data = ldphysio(pa);
3118cfb9e062Shyw 	MC_LOG("pa(%lx) = 0x%x\n", pa, data);
311925cf1a30Sjl }
312025cf1a30Sjl 
312125cf1a30Sjl uint32_t
312225cf1a30Sjl mc_ldphysio(uint64_t pa)
312325cf1a30Sjl {
312425cf1a30Sjl 	uint32_t rv;
312525cf1a30Sjl 
312625cf1a30Sjl 	rv = ldphysio(pa);
312725cf1a30Sjl 	MC_LOG("pa(%lx) = 0x%x\n", pa, rv);
312825cf1a30Sjl 	return (rv);
312925cf1a30Sjl }
31300cc8ae86Sav 
31310cc8ae86Sav #define	isdigit(ch)	((ch) >= '0' && (ch) <= '9')
31320cc8ae86Sav 
31330cc8ae86Sav /*
31340cc8ae86Sav  * parse_unum_memory -- extract the board number and the DIMM name from
31350cc8ae86Sav  * the unum.
31360cc8ae86Sav  *
31370cc8ae86Sav  * Return 0 for success and non-zero for a failure.
31380cc8ae86Sav  */
31390cc8ae86Sav int
31400cc8ae86Sav parse_unum_memory(char *unum, int *board, char *dname)
31410cc8ae86Sav {
31420cc8ae86Sav 	char *c;
31430cc8ae86Sav 	char x, y, z;
31440cc8ae86Sav 
31450cc8ae86Sav 	if ((c = strstr(unum, "CMU")) != NULL) {
31460cc8ae86Sav 		/* DC Model */
31470cc8ae86Sav 		c += 3;
31480cc8ae86Sav 		*board = (uint8_t)stoi(&c);
31490cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
31500cc8ae86Sav 			return (1);
31510cc8ae86Sav 		}
31520cc8ae86Sav 		c += 3;
31530cc8ae86Sav 		if (strlen(c) < 3) {
31540cc8ae86Sav 			return (2);
31550cc8ae86Sav 		}
31560cc8ae86Sav 		if ((!isdigit(c[0])) || (!(isdigit(c[1]))) ||
31570cc8ae86Sav 		    ((c[2] != 'A') && (c[2] != 'B'))) {
31580cc8ae86Sav 			return (3);
31590cc8ae86Sav 		}
31600cc8ae86Sav 		x = c[0];
31610cc8ae86Sav 		y = c[1];
31620cc8ae86Sav 		z = c[2];
31630cc8ae86Sav 	} else if ((c = strstr(unum, "MBU_")) != NULL) {
31640cc8ae86Sav 		/*  FF1/FF2 Model */
31650cc8ae86Sav 		c += 4;
31660cc8ae86Sav 		if ((c[0] != 'A') && (c[0] != 'B')) {
31670cc8ae86Sav 			return (4);
31680cc8ae86Sav 		}
31690cc8ae86Sav 		if ((c = strstr(c, "MEMB")) == NULL) {
31700cc8ae86Sav 			return (5);
31710cc8ae86Sav 		}
31720cc8ae86Sav 		c += 4;
31730cc8ae86Sav 
31740cc8ae86Sav 		x = c[0];
31750cc8ae86Sav 		*board =  ((uint8_t)stoi(&c)) / 4;
31760cc8ae86Sav 		if ((c = strstr(c, "MEM")) == NULL) {
31770cc8ae86Sav 			return (6);
31780cc8ae86Sav 		}
31790cc8ae86Sav 		c += 3;
31800cc8ae86Sav 		if (strlen(c) < 2) {
31810cc8ae86Sav 			return (7);
31820cc8ae86Sav 		}
31830cc8ae86Sav 		if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) {
31840cc8ae86Sav 			return (8);
31850cc8ae86Sav 		}
31860cc8ae86Sav 		y = c[0];
31870cc8ae86Sav 		z = c[1];
31880cc8ae86Sav 	} else {
31890cc8ae86Sav 		return (9);
31900cc8ae86Sav 	}
31910cc8ae86Sav 	if (*board < 0) {
31920cc8ae86Sav 		return (10);
31930cc8ae86Sav 	}
31940cc8ae86Sav 	dname[0] = x;
31950cc8ae86Sav 	dname[1] = y;
31960cc8ae86Sav 	dname[2] = z;
31970cc8ae86Sav 	dname[3] = '\0';
31980cc8ae86Sav 	return (0);
31990cc8ae86Sav }
32000cc8ae86Sav 
32010cc8ae86Sav /*
32020cc8ae86Sav  * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and
32030cc8ae86Sav  * the DIMM name.
32040cc8ae86Sav  */
32050cc8ae86Sav int
32060cc8ae86Sav mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf,
32070cc8ae86Sav     int buflen, int *lenp)
32080cc8ae86Sav {
32090cc8ae86Sav 	int		ret = ENODEV;
32100cc8ae86Sav 	mc_dimm_info_t	*d = NULL;
32110cc8ae86Sav 
32120cc8ae86Sav 	if ((d = mcp->mc_dimm_list) == NULL)
32130cc8ae86Sav 		return (ENOTSUP);
32140cc8ae86Sav 
32150cc8ae86Sav 	for (; d != NULL; d = d->md_next) {
32160cc8ae86Sav 		if (strcmp(d->md_dimmname, dname) == 0) {
32170cc8ae86Sav 			break;
32180cc8ae86Sav 		}
32190cc8ae86Sav 	}
32200cc8ae86Sav 	if (d != NULL) {
32210cc8ae86Sav 		*lenp = strlen(d->md_serial) + strlen(d->md_partnum);
32220cc8ae86Sav 		if (buflen <=  *lenp) {
32230cc8ae86Sav 			cmn_err(CE_WARN, "mc_get_mem_sid_dimm: "
32240cc8ae86Sav 			    "buflen is smaller than %d\n", *lenp);
32250cc8ae86Sav 			ret = ENOSPC;
32260cc8ae86Sav 		} else {
32270cc8ae86Sav 			snprintf(buf, buflen, "%s:%s",
32280cc8ae86Sav 			    d->md_serial, d->md_partnum);
32290cc8ae86Sav 			ret = 0;
32300cc8ae86Sav 		}
32310cc8ae86Sav 	}
32320cc8ae86Sav 	MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n",
32330cc8ae86Sav 	    ret, dname, (ret == 0) ? buf : "");
32340cc8ae86Sav 	return (ret);
32350cc8ae86Sav }
32360cc8ae86Sav 
32370cc8ae86Sav int
3238aeb241b2Sav mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int sb,
32390cc8ae86Sav     int bank, uint32_t mf_type, uint32_t d_slot)
32400cc8ae86Sav {
32410cc8ae86Sav 	int	lenp = buflen;
32420cc8ae86Sav 	int	id;
32430cc8ae86Sav 	int	ret;
32440cc8ae86Sav 	char	*dimmnm;
32450cc8ae86Sav 
32460cc8ae86Sav 	if (mf_type == FLT_TYPE_PERMANENT_CE) {
32470cc8ae86Sav 		if (plat_model == MODEL_DC) {
32480cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot);
3249aeb241b2Sav 			dimmnm = mc_dc_dimm_unum_table[id];
32500cc8ae86Sav 		} else {
32510cc8ae86Sav 			id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot);
3252aeb241b2Sav 			dimmnm = mc_ff_dimm_unum_table[id];
32530cc8ae86Sav 		}
32540cc8ae86Sav 		if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen,
32550cc8ae86Sav 		    &lenp)) != 0) {
32560cc8ae86Sav 			return (ret);
32570cc8ae86Sav 		}
32580cc8ae86Sav 	} else {
32590cc8ae86Sav 		return (1);
32600cc8ae86Sav 	}
32610cc8ae86Sav 
32620cc8ae86Sav 	return (0);
32630cc8ae86Sav }
32640cc8ae86Sav 
32650cc8ae86Sav /*
32660cc8ae86Sav  * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum.
32670cc8ae86Sav  */
32680cc8ae86Sav int
32690cc8ae86Sav mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
32700cc8ae86Sav {
32710cc8ae86Sav 	int	i;
32720cc8ae86Sav 	int	ret = ENODEV;
32730cc8ae86Sav 	int	board;
32740cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
32750cc8ae86Sav 	mc_opl_t *mcp;
32760cc8ae86Sav 
32770cc8ae86Sav 	MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen);
32780cc8ae86Sav 	if ((ret = parse_unum_memory(unum, &board, dname)) != 0) {
32790cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
32800cc8ae86Sav 		    unum, ret);
32810cc8ae86Sav 		return (EINVAL);
32820cc8ae86Sav 	}
32830cc8ae86Sav 
32840cc8ae86Sav 	if (board < 0) {
32850cc8ae86Sav 		MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n",
32860cc8ae86Sav 		    board, dname);
32870cc8ae86Sav 		return (EINVAL);
32880cc8ae86Sav 	}
32890cc8ae86Sav 
32900cc8ae86Sav 	mutex_enter(&mcmutex);
32911039f409Sav 	/*
32921039f409Sav 	 * return ENOENT if we can not find the matching board.
32931039f409Sav 	 */
32941039f409Sav 	ret = ENOENT;
32950cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
32960cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
32970cc8ae86Sav 			continue;
32980cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3299aeb241b2Sav 		if (mcp->mc_phys_board_num != board) {
3300aeb241b2Sav 			mutex_exit(&mcp->mc_lock);
3301aeb241b2Sav 			continue;
3302aeb241b2Sav 		}
3303aeb241b2Sav 		ret = mc_get_mem_sid_dimm(mcp, dname, buf, buflen, lenp);
3304aeb241b2Sav 		if (ret == 0) {
33050cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
33060cc8ae86Sav 			break;
33070cc8ae86Sav 		}
33080cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
33090cc8ae86Sav 	}
33100cc8ae86Sav 	mutex_exit(&mcmutex);
33110cc8ae86Sav 	return (ret);
33120cc8ae86Sav }
33130cc8ae86Sav 
33140cc8ae86Sav /*
33150cc8ae86Sav  * mc_get_mem_offset -- get the offset in a DIMM for a given physical address.
33160cc8ae86Sav  */
33170cc8ae86Sav int
33180cc8ae86Sav mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
33190cc8ae86Sav {
33200cc8ae86Sav 	int		i;
33210cc8ae86Sav 	int		ret = ENODEV;
33220cc8ae86Sav 	mc_addr_t	maddr;
33230cc8ae86Sav 	mc_opl_t	*mcp;
33240cc8ae86Sav 
33250cc8ae86Sav 	mutex_enter(&mcmutex);
3326c964b0e6Sraghuram 	for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) {
33270cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
33280cc8ae86Sav 			continue;
33290cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
33300cc8ae86Sav 		if (!pa_is_valid(mcp, paddr)) {
33310cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
33320cc8ae86Sav 			continue;
33330cc8ae86Sav 		}
33340cc8ae86Sav 		if (pa_to_maddr(mcp, paddr, &maddr) == 0) {
33350cc8ae86Sav 			*offp = maddr.ma_dimm_addr;
33360cc8ae86Sav 			ret = 0;
33370cc8ae86Sav 		}
33380cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
33390cc8ae86Sav 	}
33400cc8ae86Sav 	mutex_exit(&mcmutex);
33410cc8ae86Sav 	MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n",
33420cc8ae86Sav 	    ret, paddr, *offp);
33430cc8ae86Sav 	return (ret);
33440cc8ae86Sav }
33450cc8ae86Sav 
33460cc8ae86Sav /*
33470cc8ae86Sav  * dname_to_bankslot - Get the bank and slot number from the DIMM name.
33480cc8ae86Sav  */
33490cc8ae86Sav int
33500cc8ae86Sav dname_to_bankslot(char *dname, int *bank, int *slot)
33510cc8ae86Sav {
33520cc8ae86Sav 	int i;
33530cc8ae86Sav 	int tsz;
33540cc8ae86Sav 	char **tbl;
33550cc8ae86Sav 
33560cc8ae86Sav 	if (plat_model == MODEL_DC) { /* DC */
33570cc8ae86Sav 		tbl = mc_dc_dimm_unum_table;
33580cc8ae86Sav 		tsz = OPL_MAX_DIMMS;
33590cc8ae86Sav 	} else {
33600cc8ae86Sav 		tbl = mc_ff_dimm_unum_table;
33610cc8ae86Sav 		tsz = 2 * OPL_MAX_DIMMS;
33620cc8ae86Sav 	}
33630cc8ae86Sav 
33640cc8ae86Sav 	for (i = 0; i < tsz; i++) {
33650cc8ae86Sav 		if (strcmp(dname,  tbl[i]) == 0) {
33660cc8ae86Sav 			break;
33670cc8ae86Sav 		}
33680cc8ae86Sav 	}
33690cc8ae86Sav 	if (i == tsz) {
33700cc8ae86Sav 		return (1);
33710cc8ae86Sav 	}
33720cc8ae86Sav 	*bank = INDEX_TO_BANK(i);
33730cc8ae86Sav 	*slot = INDEX_TO_SLOT(i);
33740cc8ae86Sav 	return (0);
33750cc8ae86Sav }
33760cc8ae86Sav 
33770cc8ae86Sav /*
33780cc8ae86Sav  * mc_get_mem_addr -- get the physical address of a DIMM corresponding
33790cc8ae86Sav  * to the unum and sid.
33800cc8ae86Sav  */
33810cc8ae86Sav int
33820cc8ae86Sav mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr)
33830cc8ae86Sav {
33840cc8ae86Sav 	int	board;
33850cc8ae86Sav 	int	bank;
33860cc8ae86Sav 	int	slot;
33870cc8ae86Sav 	int	i;
33880cc8ae86Sav 	int	ret = ENODEV;
33890cc8ae86Sav 	char	dname[MCOPL_MAX_DIMMNAME + 1];
33900cc8ae86Sav 	mc_addr_t maddr;
33910cc8ae86Sav 	mc_opl_t *mcp;
33920cc8ae86Sav 
33930cc8ae86Sav 	MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n",
33940cc8ae86Sav 	    unum, sid, offset);
33950cc8ae86Sav 	if (parse_unum_memory(unum, &board, dname) != 0) {
33960cc8ae86Sav 		MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
33970cc8ae86Sav 		    unum, ret);
33980cc8ae86Sav 		return (EINVAL);
33990cc8ae86Sav 	}
34000cc8ae86Sav 
34010cc8ae86Sav 	if (board < 0) {
34020cc8ae86Sav 		MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n",
34030cc8ae86Sav 		    board, dname);
34040cc8ae86Sav 		return (EINVAL);
34050cc8ae86Sav 	}
34060cc8ae86Sav 
34070cc8ae86Sav 	mutex_enter(&mcmutex);
34080cc8ae86Sav 	for (i = 0; i < OPL_MAX_BOARDS; i++) {
34090cc8ae86Sav 		if ((mcp = mc_instances[i]) == NULL)
34100cc8ae86Sav 			continue;
34110cc8ae86Sav 		mutex_enter(&mcp->mc_lock);
3412aeb241b2Sav 		if (mcp->mc_phys_board_num != board) {
34130cc8ae86Sav 			mutex_exit(&mcp->mc_lock);
34140cc8ae86Sav 			continue;
34150cc8ae86Sav 		}
34160cc8ae86Sav 
34170cc8ae86Sav 		ret = dname_to_bankslot(dname, &bank, &slot);
34180cc8ae86Sav 		MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot);
34190cc8ae86Sav 		if (ret != 0) {
34200cc8ae86Sav 			MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n");
34210cc8ae86Sav 			ret = ENODEV;
34220cc8ae86Sav 		} else {
3423aeb241b2Sav 			maddr.ma_bd = mcp->mc_board_num;
34240cc8ae86Sav 			maddr.ma_bank =  bank;
34250cc8ae86Sav 			maddr.ma_dimm_addr = offset;
34260cc8ae86Sav 			ret = mcaddr_to_pa(mcp, &maddr, paddr);
34270cc8ae86Sav 			if (ret != 0) {
34280cc8ae86Sav 				MC_LOG("mc_get_mem_addr: "
34290cc8ae86Sav 				    "mcaddr_to_pa failed\n");
34300cc8ae86Sav 				ret = ENODEV;
34310cc8ae86Sav 			}
3432aeb241b2Sav 			mutex_exit(&mcp->mc_lock);
3433aeb241b2Sav 			break;
34340cc8ae86Sav 		}
34350cc8ae86Sav 		mutex_exit(&mcp->mc_lock);
34360cc8ae86Sav 	}
34370cc8ae86Sav 	mutex_exit(&mcmutex);
34380cc8ae86Sav 	MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr);
34390cc8ae86Sav 	return (ret);
34400cc8ae86Sav }
34410cc8ae86Sav 
34420cc8ae86Sav static void
34430cc8ae86Sav mc_free_dimm_list(mc_dimm_info_t *d)
34440cc8ae86Sav {
34450cc8ae86Sav 	mc_dimm_info_t *next;
34460cc8ae86Sav 
34470cc8ae86Sav 	while (d != NULL) {
34480cc8ae86Sav 		next = d->md_next;
34490cc8ae86Sav 		kmem_free(d, sizeof (mc_dimm_info_t));
34500cc8ae86Sav 		d = next;
34510cc8ae86Sav 	}
34520cc8ae86Sav }
34530cc8ae86Sav 
34540cc8ae86Sav /*
34550cc8ae86Sav  * mc_get_dimm_list -- get the list of dimms with serial-id info
34560cc8ae86Sav  * from the SP.
34570cc8ae86Sav  */
34580cc8ae86Sav mc_dimm_info_t *
34590cc8ae86Sav mc_get_dimm_list(mc_opl_t *mcp)
34600cc8ae86Sav {
34610cc8ae86Sav 	uint32_t	bufsz;
34620cc8ae86Sav 	uint32_t	maxbufsz;
34630cc8ae86Sav 	int		ret;
34640cc8ae86Sav 	int		sexp;
34650cc8ae86Sav 	board_dimm_info_t *bd_dimmp;
34660cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
34670cc8ae86Sav 
34680cc8ae86Sav 	maxbufsz = bufsz = sizeof (board_dimm_info_t) +
34690cc8ae86Sav 	    ((MCOPL_MAX_DIMMNAME +  MCOPL_MAX_SERIAL +
34700cc8ae86Sav 	    MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS);
34710cc8ae86Sav 
34720cc8ae86Sav 	bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP);
34730cc8ae86Sav 	ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz);
34740cc8ae86Sav 
34750cc8ae86Sav 	MC_LOG("mc_get_dimm_list:  scf_service_getinfo returned=%d\n", ret);
34760cc8ae86Sav 	if (ret == 0) {
34770cc8ae86Sav 		sexp = sizeof (board_dimm_info_t) +
34780cc8ae86Sav 		    ((bd_dimmp->bd_dnamesz +  bd_dimmp->bd_serialsz +
34790cc8ae86Sav 		    bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms);
34800cc8ae86Sav 
34810cc8ae86Sav 		if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) &&
34820cc8ae86Sav 		    (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) &&
34830cc8ae86Sav 		    (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) &&
34840cc8ae86Sav 		    (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) &&
34850cc8ae86Sav 		    (sexp <= bufsz)) {
34860cc8ae86Sav 
34870cc8ae86Sav #ifdef DEBUG
34880cc8ae86Sav 			if (oplmc_debug)
34890cc8ae86Sav 				mc_dump_dimm_info(bd_dimmp);
34900cc8ae86Sav #endif
34910cc8ae86Sav 			dimm_list = mc_prepare_dimmlist(bd_dimmp);
34920cc8ae86Sav 
34930cc8ae86Sav 		} else {
34940cc8ae86Sav 			cmn_err(CE_WARN, "DIMM info version mismatch\n");
34950cc8ae86Sav 		}
34960cc8ae86Sav 	}
34970cc8ae86Sav 	kmem_free(bd_dimmp, maxbufsz);
34980cc8ae86Sav 	MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list);
34990cc8ae86Sav 	return (dimm_list);
35000cc8ae86Sav }
35010cc8ae86Sav 
35020cc8ae86Sav /*
35031039f409Sav  * mc_prepare_dimmlist - Prepare the dimm list from the information
35041039f409Sav  * received from the SP.
35050cc8ae86Sav  */
35060cc8ae86Sav mc_dimm_info_t *
35070cc8ae86Sav mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp)
35080cc8ae86Sav {
35090cc8ae86Sav 	char	*dimm_name;
35100cc8ae86Sav 	char	*serial;
35110cc8ae86Sav 	char	*part;
35120cc8ae86Sav 	int	dimm;
35130cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
35140cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
35150cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
35160cc8ae86Sav 	mc_dimm_info_t	*dimm_list = NULL;
35170cc8ae86Sav 	mc_dimm_info_t	*d;
35180cc8ae86Sav 
35190cc8ae86Sav 	dimm_name = (char *)(bd_dimmp + 1);
35200cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
35210cc8ae86Sav 
35220cc8ae86Sav 		d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t),
35230cc8ae86Sav 		    KM_SLEEP);
3524ad59b69dSbm 
3525ad59b69dSbm 		bcopy(dimm_name, d->md_dimmname, dnamesz);
3526ad59b69dSbm 		d->md_dimmname[dnamesz] = 0;
3527ad59b69dSbm 
35280cc8ae86Sav 		serial = dimm_name + dnamesz;
3529ad59b69dSbm 		bcopy(serial, d->md_serial, sersz);
3530ad59b69dSbm 		d->md_serial[sersz] = 0;
3531ad59b69dSbm 
35320cc8ae86Sav 		part = serial + sersz;
3533ad59b69dSbm 		bcopy(part, d->md_partnum, partsz);
3534ad59b69dSbm 		d->md_partnum[partsz] = 0;
35350cc8ae86Sav 
35360cc8ae86Sav 		d->md_next = dimm_list;
35370cc8ae86Sav 		dimm_list = d;
35380cc8ae86Sav 		dimm_name = part + partsz;
35390cc8ae86Sav 	}
35400cc8ae86Sav 	return (dimm_list);
35410cc8ae86Sav }
35420cc8ae86Sav 
35430cc8ae86Sav #ifdef DEBUG
35440cc8ae86Sav void
35450cc8ae86Sav mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz)
35460cc8ae86Sav {
35470cc8ae86Sav 	char dname[MCOPL_MAX_DIMMNAME + 1];
35480cc8ae86Sav 	char serial[MCOPL_MAX_SERIAL + 1];
35490cc8ae86Sav 	char part[ MCOPL_MAX_PARTNUM + 1];
35500cc8ae86Sav 	char *b;
35510cc8ae86Sav 
35520cc8ae86Sav 	b = buf;
3553ad59b69dSbm 	bcopy(b, dname, dnamesz);
3554ad59b69dSbm 	dname[dnamesz] = 0;
3555ad59b69dSbm 
35560cc8ae86Sav 	b += dnamesz;
3557ad59b69dSbm 	bcopy(b, serial, serialsz);
3558ad59b69dSbm 	serial[serialsz] = 0;
3559ad59b69dSbm 
35600cc8ae86Sav 	b += serialsz;
3561ad59b69dSbm 	bcopy(b, part, partnumsz);
3562ad59b69dSbm 	part[partnumsz] = 0;
3563ad59b69dSbm 
35640cc8ae86Sav 	printf("DIMM=%s  Serial=%s PartNum=%s\n", dname, serial, part);
35650cc8ae86Sav }
35660cc8ae86Sav 
35670cc8ae86Sav void
35680cc8ae86Sav mc_dump_dimm_info(board_dimm_info_t *bd_dimmp)
35690cc8ae86Sav {
35700cc8ae86Sav 	int	dimm;
35710cc8ae86Sav 	int	dnamesz = bd_dimmp->bd_dnamesz;
35720cc8ae86Sav 	int	sersz = bd_dimmp->bd_serialsz;
35730cc8ae86Sav 	int	partsz = bd_dimmp->bd_partnumsz;
35740cc8ae86Sav 	char	*buf;
35750cc8ae86Sav 
35760cc8ae86Sav 	printf("Version=%d Board=%02d DIMMs=%d NameSize=%d "
35770cc8ae86Sav 	    "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version,
35780cc8ae86Sav 	    bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz,
35790cc8ae86Sav 	    bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz);
35800cc8ae86Sav 	printf("======================================================\n");
35810cc8ae86Sav 
35820cc8ae86Sav 	buf = (char *)(bd_dimmp + 1);
35830cc8ae86Sav 	for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) {
35840cc8ae86Sav 		mc_dump_dimm(buf, dnamesz, sersz, partsz);
35850cc8ae86Sav 		buf += dnamesz + sersz + partsz;
35860cc8ae86Sav 	}
35870cc8ae86Sav 	printf("======================================================\n");
35880cc8ae86Sav }
35890cc8ae86Sav 
35900cc8ae86Sav 
35910cc8ae86Sav /* ARGSUSED */
35920cc8ae86Sav static int
35930cc8ae86Sav mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
35940cc8ae86Sav 	int *rvalp)
35950cc8ae86Sav {
35960cc8ae86Sav 	caddr_t	buf;
35970cc8ae86Sav 	uint64_t pa;
35980cc8ae86Sav 	int rv = 0;
35990cc8ae86Sav 	int i;
36000cc8ae86Sav 	uint32_t flags;
36010cc8ae86Sav 	static uint32_t offset = 0;
36020cc8ae86Sav 
36030cc8ae86Sav 
36040cc8ae86Sav 	flags = (cmd >> 4) & 0xfffffff;
36050cc8ae86Sav 
36060cc8ae86Sav 	cmd &= 0xf;
36070cc8ae86Sav 
36080cc8ae86Sav 	MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags);
36090cc8ae86Sav 
36100cc8ae86Sav 	if (arg != NULL) {
36110cc8ae86Sav 		if (ddi_copyin((const void *)arg, (void *)&pa,
3612*d8a0cca9Swh 		    sizeof (uint64_t), 0) < 0) {
36130cc8ae86Sav 			rv = EFAULT;
36140cc8ae86Sav 			return (rv);
36150cc8ae86Sav 		}
36160cc8ae86Sav 		buf = NULL;
36170cc8ae86Sav 	} else {
36180cc8ae86Sav 		buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP);
36190cc8ae86Sav 
36200cc8ae86Sav 		pa = va_to_pa(buf);
36210cc8ae86Sav 		pa += offset;
36220cc8ae86Sav 
36230cc8ae86Sav 		offset += 64;
36240cc8ae86Sav 		if (offset >= PAGESIZE)
36250cc8ae86Sav 			offset = 0;
36260cc8ae86Sav 	}
36270cc8ae86Sav 
36280cc8ae86Sav 	switch (cmd) {
36290cc8ae86Sav 	case MCI_CE:
3630*d8a0cca9Swh 		mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa, flags);
36310cc8ae86Sav 		break;
36320cc8ae86Sav 	case MCI_PERM_CE:
3633*d8a0cca9Swh 		mc_inject_error(MC_INJECT_PERMANENT_CE, pa, flags);
36340cc8ae86Sav 		break;
36350cc8ae86Sav 	case MCI_UE:
3636*d8a0cca9Swh 		mc_inject_error(MC_INJECT_UE, pa, flags);
36370cc8ae86Sav 		break;
36380cc8ae86Sav 	case MCI_M_CE:
3639*d8a0cca9Swh 		mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa, flags);
36400cc8ae86Sav 		break;
36410cc8ae86Sav 	case MCI_M_PCE:
3642*d8a0cca9Swh 		mc_inject_error(MC_INJECT_PERMANENT_MCE, pa, flags);
36430cc8ae86Sav 		break;
36440cc8ae86Sav 	case MCI_M_UE:
3645*d8a0cca9Swh 		mc_inject_error(MC_INJECT_MUE, pa, flags);
36460cc8ae86Sav 		break;
36470cc8ae86Sav 	case MCI_CMP:
3648*d8a0cca9Swh 		mc_inject_error(MC_INJECT_CMPE, pa, flags);
36490cc8ae86Sav 		break;
36500cc8ae86Sav 	case MCI_NOP:
3651*d8a0cca9Swh 		mc_inject_error(MC_INJECT_NOP, pa, flags); break;
36520cc8ae86Sav 	case MCI_SHOW_ALL:
36530cc8ae86Sav 		mc_debug_show_all = 1;
36540cc8ae86Sav 		break;
36550cc8ae86Sav 	case MCI_SHOW_NONE:
36560cc8ae86Sav 		mc_debug_show_all = 0;
36570cc8ae86Sav 		break;
36580cc8ae86Sav 	case MCI_ALLOC:
36590cc8ae86Sav 		/*
36600cc8ae86Sav 		 * just allocate some kernel memory and never free it
36610cc8ae86Sav 		 * 512 MB seems to be the maximum size supported.
36620cc8ae86Sav 		 */
36630cc8ae86Sav 		cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512);
36640cc8ae86Sav 		for (i = 0; i < flags; i++) {
36650cc8ae86Sav 			buf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP);
36660cc8ae86Sav 			cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n",
3667*d8a0cca9Swh 			    (u_longlong_t)buf, (u_longlong_t)va_to_pa(buf));
36680cc8ae86Sav 		}
36690cc8ae86Sav 		break;
36700cc8ae86Sav 	case MCI_SUSPEND:
36710cc8ae86Sav 		(void) opl_mc_suspend();
36720cc8ae86Sav 		break;
36730cc8ae86Sav 	case MCI_RESUME:
36740cc8ae86Sav 		(void) opl_mc_resume();
36750cc8ae86Sav 		break;
36760cc8ae86Sav 	default:
36770cc8ae86Sav 		rv = ENXIO;
36780cc8ae86Sav 	}
36790cc8ae86Sav 	return (rv);
36800cc8ae86Sav }
36810cc8ae86Sav 
36820cc8ae86Sav #endif /* DEBUG */
3683