xref: /illumos-gate/usr/src/uts/sun4u/opl/io/mc-opl.c (revision 2742aa22)
125cf1a30Sjl /*
225cf1a30Sjl  * CDDL HEADER START
325cf1a30Sjl  *
425cf1a30Sjl  * The contents of this file are subject to the terms of the
525cf1a30Sjl  * Common Development and Distribution License (the "License").
625cf1a30Sjl  * You may not use this file except in compliance with the License.
725cf1a30Sjl  *
825cf1a30Sjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
925cf1a30Sjl  * or http://www.opensolaris.org/os/licensing.
1025cf1a30Sjl  * See the License for the specific language governing permissions
1125cf1a30Sjl  * and limitations under the License.
1225cf1a30Sjl  *
1325cf1a30Sjl  * When distributing Covered Code, include this CDDL HEADER in each
1425cf1a30Sjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1525cf1a30Sjl  * If applicable, add the following below this CDDL HEADER, with the
1625cf1a30Sjl  * fields enclosed by brackets "[]" replaced with your own identifying
1725cf1a30Sjl  * information: Portions Copyright [yyyy] [name of copyright owner]
1825cf1a30Sjl  *
1925cf1a30Sjl  * CDDL HEADER END
2025cf1a30Sjl  */
2125cf1a30Sjl /*
2225cf1a30Sjl  * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006
2325cf1a30Sjl  */
2425cf1a30Sjl 
2525cf1a30Sjl #pragma ident	"%Z%%M%	%I%	%E% SMI"
2625cf1a30Sjl 
2725cf1a30Sjl #include <sys/types.h>
2825cf1a30Sjl #include <sys/sysmacros.h>
2925cf1a30Sjl #include <sys/conf.h>
3025cf1a30Sjl #include <sys/modctl.h>
3125cf1a30Sjl #include <sys/stat.h>
3225cf1a30Sjl #include <sys/async.h>
3325cf1a30Sjl #include <sys/machsystm.h>
3425cf1a30Sjl #include <sys/ksynch.h>
3525cf1a30Sjl #include <sys/ddi.h>
3625cf1a30Sjl #include <sys/sunddi.h>
3725cf1a30Sjl #include <sys/ddifm.h>
3825cf1a30Sjl #include <sys/fm/protocol.h>
3925cf1a30Sjl #include <sys/fm/util.h>
4025cf1a30Sjl #include <sys/kmem.h>
4125cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h>
4225cf1a30Sjl #include <sys/memlist.h>
4325cf1a30Sjl #include <sys/param.h>
4425cf1a30Sjl #include <sys/ontrap.h>
4525cf1a30Sjl #include <vm/page.h>
4625cf1a30Sjl #include <sys/mc-opl.h>
4725cf1a30Sjl 
4825cf1a30Sjl /*
4925cf1a30Sjl  * Function prototypes
5025cf1a30Sjl  */
5125cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *);
5225cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *);
5325cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
5425cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
5525cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
5625cf1a30Sjl 
5725cf1a30Sjl static int mc_board_add(mc_opl_t *mcp);
5825cf1a30Sjl static int mc_board_del(mc_opl_t *mcp);
5925cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag);
6025cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag);
6125cf1a30Sjl 
6225cf1a30Sjl static void insert_mcp(mc_opl_t *mcp);
6325cf1a30Sjl static void delete_mcp(mc_opl_t *mcp);
6425cf1a30Sjl 
6525cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr);
6625cf1a30Sjl 
6725cf1a30Sjl static int mc_valid_pa(mc_opl_t *mcp, uint64_t pa);
6825cf1a30Sjl 
6925cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *);
7025cf1a30Sjl extern int plat_max_boards(void);
7125cf1a30Sjl 
7225cf1a30Sjl static void mc_get_mlist(mc_opl_t *);
7325cf1a30Sjl 
7425cf1a30Sjl #pragma weak opl_get_physical_board
7525cf1a30Sjl extern int opl_get_physical_board(int);
7625cf1a30Sjl static int mc_opl_get_physical_board(int);
7725cf1a30Sjl 
7825cf1a30Sjl /*
7925cf1a30Sjl  * Configuration data structures
8025cf1a30Sjl  */
8125cf1a30Sjl static struct cb_ops mc_cb_ops = {
8225cf1a30Sjl 	mc_open,			/* open */
8325cf1a30Sjl 	mc_close,			/* close */
8425cf1a30Sjl 	nulldev,			/* strategy */
8525cf1a30Sjl 	nulldev,			/* print */
8625cf1a30Sjl 	nodev,				/* dump */
8725cf1a30Sjl 	nulldev,			/* read */
8825cf1a30Sjl 	nulldev,			/* write */
8925cf1a30Sjl 	mc_ioctl,			/* ioctl */
9025cf1a30Sjl 	nodev,				/* devmap */
9125cf1a30Sjl 	nodev,				/* mmap */
9225cf1a30Sjl 	nodev,				/* segmap */
9325cf1a30Sjl 	nochpoll,			/* poll */
9425cf1a30Sjl 	ddi_prop_op,			/* cb_prop_op */
9525cf1a30Sjl 	0,				/* streamtab */
9625cf1a30Sjl 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
9725cf1a30Sjl 	CB_REV,				/* rev */
9825cf1a30Sjl 	nodev,				/* cb_aread */
9925cf1a30Sjl 	nodev				/* cb_awrite */
10025cf1a30Sjl };
10125cf1a30Sjl 
10225cf1a30Sjl static struct dev_ops mc_ops = {
10325cf1a30Sjl 	DEVO_REV,			/* rev */
10425cf1a30Sjl 	0,				/* refcnt  */
10525cf1a30Sjl 	ddi_getinfo_1to1,		/* getinfo */
10625cf1a30Sjl 	nulldev,			/* identify */
10725cf1a30Sjl 	nulldev,			/* probe */
10825cf1a30Sjl 	mc_attach,			/* attach */
10925cf1a30Sjl 	mc_detach,			/* detach */
11025cf1a30Sjl 	nulldev,			/* reset */
11125cf1a30Sjl 	&mc_cb_ops,			/* cb_ops */
11225cf1a30Sjl 	(struct bus_ops *)0,		/* bus_ops */
11325cf1a30Sjl 	nulldev				/* power */
11425cf1a30Sjl };
11525cf1a30Sjl 
11625cf1a30Sjl /*
11725cf1a30Sjl  * Driver globals
11825cf1a30Sjl  */
11925cf1a30Sjl int mc_patrol_interval_sec = 10;
12025cf1a30Sjl 
12125cf1a30Sjl int inject_op_delay = 5;
12225cf1a30Sjl 
12325cf1a30Sjl mc_inst_list_t *mc_instances;
12425cf1a30Sjl static kmutex_t mcmutex;
12525cf1a30Sjl 
12625cf1a30Sjl void *mc_statep;
12725cf1a30Sjl 
12825cf1a30Sjl #ifdef	DEBUG
129*2742aa22Shyw int oplmc_debug = 0;
13025cf1a30Sjl #endif
13125cf1a30Sjl 
13225cf1a30Sjl static int mc_debug_show_all;
13325cf1a30Sjl 
13425cf1a30Sjl extern struct mod_ops mod_driverops;
13525cf1a30Sjl 
13625cf1a30Sjl static struct modldrv modldrv = {
13725cf1a30Sjl 	&mod_driverops,			/* module type, this one is a driver */
13825cf1a30Sjl 	"OPL Memory-controller 1.1",	/* module name */
13925cf1a30Sjl 	&mc_ops,			/* driver ops */
14025cf1a30Sjl };
14125cf1a30Sjl 
14225cf1a30Sjl static struct modlinkage modlinkage = {
14325cf1a30Sjl 	MODREV_1,		/* rev */
14425cf1a30Sjl 	(void *)&modldrv,
14525cf1a30Sjl 	NULL
14625cf1a30Sjl };
14725cf1a30Sjl 
14825cf1a30Sjl #pragma weak opl_get_mem_unum
14925cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *);
15025cf1a30Sjl 
15125cf1a30Sjl /*
15225cf1a30Sjl  * pseudo-mc node portid format
15325cf1a30Sjl  *
15425cf1a30Sjl  *		[10]   = 0
15525cf1a30Sjl  *		[9]    = 1
15625cf1a30Sjl  *		[8]    = LSB_ID[4] = 0
15725cf1a30Sjl  *		[7:4]  = LSB_ID[3:0]
15825cf1a30Sjl  *		[3:0]  = 0
15925cf1a30Sjl  *
16025cf1a30Sjl  */
16125cf1a30Sjl 
16225cf1a30Sjl /*
16325cf1a30Sjl  * These are the module initialization routines.
16425cf1a30Sjl  */
16525cf1a30Sjl int
16625cf1a30Sjl _init(void)
16725cf1a30Sjl {
16825cf1a30Sjl 	int error;
16925cf1a30Sjl 
17025cf1a30Sjl 
17125cf1a30Sjl 	if ((error = ddi_soft_state_init(&mc_statep,
17225cf1a30Sjl 	    sizeof (mc_opl_t), 1)) != 0)
17325cf1a30Sjl 		return (error);
17425cf1a30Sjl 
17525cf1a30Sjl 	mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
17625cf1a30Sjl 	if (&opl_get_mem_unum)
17725cf1a30Sjl 		opl_get_mem_unum = mc_get_mem_unum;
17825cf1a30Sjl 
17925cf1a30Sjl 	error =  mod_install(&modlinkage);
18025cf1a30Sjl 	if (error != 0) {
18125cf1a30Sjl 		if (&opl_get_mem_unum)
18225cf1a30Sjl 			opl_get_mem_unum = NULL;
18325cf1a30Sjl 		mutex_destroy(&mcmutex);
18425cf1a30Sjl 		ddi_soft_state_fini(&mc_statep);
18525cf1a30Sjl 	}
18625cf1a30Sjl 
18725cf1a30Sjl 	return (error);
18825cf1a30Sjl }
18925cf1a30Sjl 
19025cf1a30Sjl int
19125cf1a30Sjl _fini(void)
19225cf1a30Sjl {
19325cf1a30Sjl 	int error;
19425cf1a30Sjl 
19525cf1a30Sjl 	if ((error = mod_remove(&modlinkage)) != 0)
19625cf1a30Sjl 		return (error);
19725cf1a30Sjl 
19825cf1a30Sjl 	mutex_destroy(&mcmutex);
19925cf1a30Sjl 
20025cf1a30Sjl 	if (&opl_get_mem_unum)
20125cf1a30Sjl 		opl_get_mem_unum = NULL;
20225cf1a30Sjl 
20325cf1a30Sjl 	ddi_soft_state_fini(&mc_statep);
20425cf1a30Sjl 
20525cf1a30Sjl 	return (0);
20625cf1a30Sjl }
20725cf1a30Sjl 
20825cf1a30Sjl int
20925cf1a30Sjl _info(struct modinfo *modinfop)
21025cf1a30Sjl {
21125cf1a30Sjl 	return (mod_info(&modlinkage, modinfop));
21225cf1a30Sjl }
21325cf1a30Sjl 
21425cf1a30Sjl static int
21525cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
21625cf1a30Sjl {
21725cf1a30Sjl 	mc_opl_t *mcp;
21825cf1a30Sjl 	int instance;
21925cf1a30Sjl 
22025cf1a30Sjl 	/* get the instance of this devi */
22125cf1a30Sjl 	instance = ddi_get_instance(devi);
22225cf1a30Sjl 
22325cf1a30Sjl 	switch (cmd) {
22425cf1a30Sjl 	case DDI_ATTACH:
22525cf1a30Sjl 		break;
22625cf1a30Sjl 	case DDI_RESUME:
22725cf1a30Sjl 		mcp = ddi_get_soft_state(mc_statep, instance);
22825cf1a30Sjl 		return (mc_resume(mcp, MC_DRIVER_SUSPENDED));
22925cf1a30Sjl 	default:
23025cf1a30Sjl 		return (DDI_FAILURE);
23125cf1a30Sjl 	}
23225cf1a30Sjl 
23325cf1a30Sjl 
23425cf1a30Sjl 	if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS)
23525cf1a30Sjl 		return (DDI_FAILURE);
23625cf1a30Sjl 
23725cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
23825cf1a30Sjl 		goto bad;
23925cf1a30Sjl 	}
24025cf1a30Sjl 
24125cf1a30Sjl 	/* set informations in mc state */
24225cf1a30Sjl 	mcp->mc_dip = devi;
24325cf1a30Sjl 
24425cf1a30Sjl 	if (mc_board_add(mcp))
24525cf1a30Sjl 		goto bad;
24625cf1a30Sjl 
24725cf1a30Sjl 	insert_mcp(mcp);
24825cf1a30Sjl 	ddi_report_dev(devi);
24925cf1a30Sjl 
25025cf1a30Sjl 	return (DDI_SUCCESS);
25125cf1a30Sjl 
25225cf1a30Sjl bad:
25325cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
25425cf1a30Sjl 	return (DDI_FAILURE);
25525cf1a30Sjl }
25625cf1a30Sjl 
25725cf1a30Sjl /* ARGSUSED */
25825cf1a30Sjl static int
25925cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
26025cf1a30Sjl {
26125cf1a30Sjl 	int instance;
26225cf1a30Sjl 	mc_opl_t *mcp;
26325cf1a30Sjl 
26425cf1a30Sjl 	/* get the instance of this devi */
26525cf1a30Sjl 	instance = ddi_get_instance(devi);
26625cf1a30Sjl 	if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) {
26725cf1a30Sjl 		return (DDI_FAILURE);
26825cf1a30Sjl 	}
26925cf1a30Sjl 
27025cf1a30Sjl 	switch (cmd) {
27125cf1a30Sjl 	case DDI_SUSPEND:
27225cf1a30Sjl 		return (mc_suspend(mcp, MC_DRIVER_SUSPENDED));
27325cf1a30Sjl 	case DDI_DETACH:
27425cf1a30Sjl 		break;
27525cf1a30Sjl 	default:
27625cf1a30Sjl 		return (DDI_FAILURE);
27725cf1a30Sjl 	}
27825cf1a30Sjl 
27925cf1a30Sjl 	mutex_enter(&mcmutex);
28025cf1a30Sjl 	if (mc_board_del(mcp) != DDI_SUCCESS) {
28125cf1a30Sjl 		mutex_exit(&mcmutex);
28225cf1a30Sjl 		return (DDI_FAILURE);
28325cf1a30Sjl 	}
28425cf1a30Sjl 
28525cf1a30Sjl 	delete_mcp(mcp);
28625cf1a30Sjl 	mutex_exit(&mcmutex);
28725cf1a30Sjl 
28825cf1a30Sjl 	/* free up the soft state */
28925cf1a30Sjl 	ddi_soft_state_free(mc_statep, instance);
29025cf1a30Sjl 
29125cf1a30Sjl 	return (DDI_SUCCESS);
29225cf1a30Sjl }
29325cf1a30Sjl 
29425cf1a30Sjl /* ARGSUSED */
29525cf1a30Sjl static int
29625cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
29725cf1a30Sjl {
29825cf1a30Sjl 	return (0);
29925cf1a30Sjl }
30025cf1a30Sjl 
30125cf1a30Sjl /* ARGSUSED */
30225cf1a30Sjl static int
30325cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
30425cf1a30Sjl {
30525cf1a30Sjl 	return (0);
30625cf1a30Sjl }
30725cf1a30Sjl 
30825cf1a30Sjl /* ARGSUSED */
30925cf1a30Sjl static int
31025cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
31125cf1a30Sjl 	int *rvalp)
31225cf1a30Sjl {
31325cf1a30Sjl 	return (ENXIO);
31425cf1a30Sjl }
31525cf1a30Sjl 
31625cf1a30Sjl /*
31725cf1a30Sjl  * PA validity check:
31825cf1a30Sjl  * This function return 1 if the PA is valid, otherwise
31925cf1a30Sjl  * return 0.
32025cf1a30Sjl  */
32125cf1a30Sjl 
32225cf1a30Sjl /* ARGSUSED */
32325cf1a30Sjl static int
32425cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr)
32525cf1a30Sjl {
32625cf1a30Sjl 	/*
32725cf1a30Sjl 	 * Check if the addr is on the board.
32825cf1a30Sjl 	 */
32925cf1a30Sjl 	if ((addr < mcp->mc_start_address) ||
33025cf1a30Sjl 	    (mcp->mc_start_address + mcp->mc_size <= addr))
33125cf1a30Sjl 		return (0);
33225cf1a30Sjl 
33325cf1a30Sjl 	if (mcp->mlist == NULL)
33425cf1a30Sjl 		mc_get_mlist(mcp);
33525cf1a30Sjl 
33625cf1a30Sjl 	if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) {
33725cf1a30Sjl 		return (1);
33825cf1a30Sjl 	}
33925cf1a30Sjl 	return (0);
34025cf1a30Sjl }
34125cf1a30Sjl 
34225cf1a30Sjl /*
34325cf1a30Sjl  * mac-pa translation routines.
34425cf1a30Sjl  *
34525cf1a30Sjl  *    Input: mc driver state, (LSB#, Bank#, DIMM address)
34625cf1a30Sjl  *    Output: physical address
34725cf1a30Sjl  *
34825cf1a30Sjl  *    Valid   - return value:  0
34925cf1a30Sjl  *    Invalid - return value: -1
35025cf1a30Sjl  */
35125cf1a30Sjl static int
35225cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa)
35325cf1a30Sjl {
35425cf1a30Sjl 	int i;
35525cf1a30Sjl 	uint64_t pa_offset = 0;
35625cf1a30Sjl 	int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1;
35725cf1a30Sjl 	int bank = maddr->ma_bank;
35825cf1a30Sjl 	mc_addr_t maddr1;
35925cf1a30Sjl 	int bank0, bank1;
36025cf1a30Sjl 
36125cf1a30Sjl 	MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank,
36225cf1a30Sjl 		maddr->ma_dimm_addr);
36325cf1a30Sjl 
36425cf1a30Sjl 	/* loc validity check */
36525cf1a30Sjl 	ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd);
36625cf1a30Sjl 	ASSERT(bank >= 0 && OPL_BANK_MAX > bank);
36725cf1a30Sjl 
36825cf1a30Sjl 	/* Do translation */
36925cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
37025cf1a30Sjl 		int pa_bit = 0;
37125cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
37225cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
37325cf1a30Sjl 			pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1;
37425cf1a30Sjl 		} else if (mc_bit == MP_NONE) {
37525cf1a30Sjl 			pa_bit = 0;
37625cf1a30Sjl 		} else if (mc_bit == MP_BANK_0) {
37725cf1a30Sjl 			pa_bit = bank & 1;
37825cf1a30Sjl 		} else if (mc_bit == MP_BANK_1) {
37925cf1a30Sjl 			pa_bit = (bank >> 1) & 1;
38025cf1a30Sjl 		} else if (mc_bit == MP_BANK_2) {
38125cf1a30Sjl 			pa_bit = (bank >> 2) & 1;
38225cf1a30Sjl 		}
38325cf1a30Sjl 		pa_offset |= ((uint64_t)pa_bit) << i;
38425cf1a30Sjl 	}
38525cf1a30Sjl 	*pa = mcp->mc_start_address + pa_offset;
38625cf1a30Sjl 	MC_LOG("pa = %lx\n", *pa);
38725cf1a30Sjl 
38825cf1a30Sjl 	if (pa_to_maddr(mcp, *pa, &maddr1) == -1) {
38925cf1a30Sjl 		return (-1);
39025cf1a30Sjl 	}
39125cf1a30Sjl 
39225cf1a30Sjl 
39325cf1a30Sjl 	if (IS_MIRROR(mcp, maddr->ma_bank)) {
39425cf1a30Sjl 		bank0 = maddr->ma_bank & ~(1);
39525cf1a30Sjl 		bank1 = maddr1.ma_bank & ~(1);
39625cf1a30Sjl 	} else {
39725cf1a30Sjl 		bank0 = maddr->ma_bank;
39825cf1a30Sjl 		bank1 = maddr1.ma_bank;
39925cf1a30Sjl 	}
40025cf1a30Sjl 	/*
40125cf1a30Sjl 	 * there is no need to check ma_bd because it is generated from
40225cf1a30Sjl 	 * mcp.  They are the same.
40325cf1a30Sjl 	 */
40425cf1a30Sjl 	if ((bank0 == bank1) &&
40525cf1a30Sjl 		(maddr->ma_dimm_addr == maddr1.ma_dimm_addr)) {
40625cf1a30Sjl 		return (0);
40725cf1a30Sjl 	} else {
40825cf1a30Sjl 		cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, "
40925cf1a30Sjl 			"PA %lx, target /LSB%d/B%d/%x\n",
41025cf1a30Sjl 			maddr->ma_bd, bank, maddr->ma_dimm_addr,
41125cf1a30Sjl 			*pa, maddr1.ma_bd, maddr1.ma_bank,
41225cf1a30Sjl 			maddr1.ma_dimm_addr);
41325cf1a30Sjl 		return (-1);
41425cf1a30Sjl 	}
41525cf1a30Sjl }
41625cf1a30Sjl 
41725cf1a30Sjl /*
41825cf1a30Sjl  * PA to CS (used by pa_to_maddr).
41925cf1a30Sjl  */
42025cf1a30Sjl static int
42125cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset)
42225cf1a30Sjl {
42325cf1a30Sjl 	int i;
42425cf1a30Sjl 	int cs = 0;
42525cf1a30Sjl 
42625cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
42725cf1a30Sjl 		/* MAC address bit<29> is arranged on the same PA bit */
42825cf1a30Sjl 		/* on both table. So we may use any table. */
42925cf1a30Sjl 		if (mcp->mc_trans_table[0][i] == CS_SHIFT) {
43025cf1a30Sjl 			cs = (pa_offset >> i) & 1;
43125cf1a30Sjl 			break;
43225cf1a30Sjl 		}
43325cf1a30Sjl 	}
43425cf1a30Sjl 	return (cs);
43525cf1a30Sjl }
43625cf1a30Sjl 
43725cf1a30Sjl /*
43825cf1a30Sjl  * PA to DIMM (used by pa_to_maddr).
43925cf1a30Sjl  */
44025cf1a30Sjl /* ARGSUSED */
44125cf1a30Sjl static uint32_t
44225cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset)
44325cf1a30Sjl {
44425cf1a30Sjl 	int i;
44525cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
44625cf1a30Sjl 	uint32_t dimm_addr = 0;
44725cf1a30Sjl 
44825cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
44925cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
45025cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
45125cf1a30Sjl 		if (mc_bit < MC_ADDRESS_BITS) {
45225cf1a30Sjl 			dimm_addr |= pa_bit_value << mc_bit;
45325cf1a30Sjl 		}
45425cf1a30Sjl 	}
45525cf1a30Sjl 	return (dimm_addr);
45625cf1a30Sjl }
45725cf1a30Sjl 
45825cf1a30Sjl /*
45925cf1a30Sjl  * PA to Bank (used by pa_to_maddr).
46025cf1a30Sjl  */
46125cf1a30Sjl static int
46225cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset)
46325cf1a30Sjl {
46425cf1a30Sjl 	int i;
46525cf1a30Sjl 	int cs = pa_to_cs(mcp, pa_offset);
46625cf1a30Sjl 	int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT];
46725cf1a30Sjl 
46825cf1a30Sjl 
46925cf1a30Sjl 	for (i = 0; i < PA_BITS_FOR_MAC; i++) {
47025cf1a30Sjl 		int pa_bit_value = (pa_offset >> i) & 1;
47125cf1a30Sjl 		int mc_bit = mcp->mc_trans_table[cs][i];
47225cf1a30Sjl 		switch (mc_bit) {
47325cf1a30Sjl 		case MP_BANK_0:
47425cf1a30Sjl 			bankno |= pa_bit_value;
47525cf1a30Sjl 			break;
47625cf1a30Sjl 		case MP_BANK_1:
47725cf1a30Sjl 			bankno |= pa_bit_value << 1;
47825cf1a30Sjl 			break;
47925cf1a30Sjl 		case MP_BANK_2:
48025cf1a30Sjl 			bankno |= pa_bit_value << 2;
48125cf1a30Sjl 			break;
48225cf1a30Sjl 		}
48325cf1a30Sjl 	}
48425cf1a30Sjl 
48525cf1a30Sjl 	return (bankno);
48625cf1a30Sjl }
48725cf1a30Sjl 
48825cf1a30Sjl /*
48925cf1a30Sjl  * PA to MAC address translation
49025cf1a30Sjl  *
49125cf1a30Sjl  *   Input: MAC driver state, physicall adress
49225cf1a30Sjl  *   Output: LSB#, Bank id, mac address
49325cf1a30Sjl  *
49425cf1a30Sjl  *    Valid   - return value:  0
49525cf1a30Sjl  *    Invalid - return value: -1
49625cf1a30Sjl  */
49725cf1a30Sjl 
49825cf1a30Sjl int
49925cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr)
50025cf1a30Sjl {
50125cf1a30Sjl 	uint64_t pa_offset;
50225cf1a30Sjl 
50325cf1a30Sjl 	/* PA validity check */
50425cf1a30Sjl 	if (!pa_is_valid(mcp, pa))
50525cf1a30Sjl 		return (-1);
50625cf1a30Sjl 
50725cf1a30Sjl 
50825cf1a30Sjl 	/* Do translation */
50925cf1a30Sjl 	pa_offset = pa - mcp->mc_start_address;
51025cf1a30Sjl 
51125cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
51225cf1a30Sjl 	maddr->ma_bank = pa_to_bank(mcp, pa_offset);
51325cf1a30Sjl 	maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset);
51425cf1a30Sjl 	MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n",
51525cf1a30Sjl 		pa_offset, maddr->ma_bd, maddr->ma_bank, maddr->ma_dimm_addr);
51625cf1a30Sjl 	return (0);
51725cf1a30Sjl }
51825cf1a30Sjl 
51925cf1a30Sjl static void
52025cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt)
52125cf1a30Sjl {
52225cf1a30Sjl 	char buf[FM_MAX_CLASS];
52325cf1a30Sjl 	char device_path[MAXPATHLEN];
52425cf1a30Sjl 	nv_alloc_t *nva = NULL;
52525cf1a30Sjl 	nvlist_t *ereport, *detector, *resource;
52625cf1a30Sjl 	errorq_elem_t *eqep;
52725cf1a30Sjl 	int nflts;
52825cf1a30Sjl 	mc_flt_stat_t *flt_stat;
52925cf1a30Sjl 	int i, n, blen;
53025cf1a30Sjl 	char *p;
53125cf1a30Sjl 	uint32_t values[2], synd[2], dslot[2];
53225cf1a30Sjl 
53325cf1a30Sjl 	if (panicstr) {
53425cf1a30Sjl 		eqep = errorq_reserve(ereport_errorq);
53525cf1a30Sjl 		if (eqep == NULL)
53625cf1a30Sjl 			return;
53725cf1a30Sjl 		ereport = errorq_elem_nvl(ereport_errorq, eqep);
53825cf1a30Sjl 		nva = errorq_elem_nva(ereport_errorq, eqep);
53925cf1a30Sjl 	} else {
54025cf1a30Sjl 		ereport = fm_nvlist_create(nva);
54125cf1a30Sjl 	}
54225cf1a30Sjl 
54325cf1a30Sjl 	/*
54425cf1a30Sjl 	 * Create the scheme "dev" FMRI.
54525cf1a30Sjl 	 */
54625cf1a30Sjl 	detector = fm_nvlist_create(nva);
54725cf1a30Sjl 	resource = fm_nvlist_create(nva);
54825cf1a30Sjl 
54925cf1a30Sjl 	nflts = mc_aflt->mflt_nflts;
55025cf1a30Sjl 
55125cf1a30Sjl 	ASSERT(nflts >= 1 && nflts <= 2);
55225cf1a30Sjl 
55325cf1a30Sjl 	flt_stat = mc_aflt->mflt_stat[0];
55425cf1a30Sjl 	(void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path);
55525cf1a30Sjl 	(void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL,
55625cf1a30Sjl 	    device_path, NULL);
55725cf1a30Sjl 
55825cf1a30Sjl 	/*
55925cf1a30Sjl 	 * Encode all the common data into the ereport.
56025cf1a30Sjl 	 */
56125cf1a30Sjl 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s",
56225cf1a30Sjl 		MC_OPL_ERROR_CLASS,
56325cf1a30Sjl 		mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS :
56425cf1a30Sjl 		MC_OPL_MI_SUBCLASS,
56525cf1a30Sjl 		mc_aflt->mflt_erpt_class);
56625cf1a30Sjl 
56725cf1a30Sjl 	MC_LOG("mc_ereport_post: ereport %s\n", buf);
56825cf1a30Sjl 
56925cf1a30Sjl 
57025cf1a30Sjl 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
57125cf1a30Sjl 		fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1),
57225cf1a30Sjl 		detector, NULL);
57325cf1a30Sjl 
57425cf1a30Sjl 	/*
57525cf1a30Sjl 	 * Set payload.
57625cf1a30Sjl 	 */
57725cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32,
57825cf1a30Sjl 		flt_stat->mf_flt_maddr.ma_bd, NULL);
57925cf1a30Sjl 
58025cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64,
58125cf1a30Sjl 		flt_stat->mf_flt_paddr, NULL);
58225cf1a30Sjl 
58325cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
58425cf1a30Sjl 		fm_payload_set(ereport, MC_OPL_FLT_TYPE,
58525cf1a30Sjl 			DATA_TYPE_UINT8, ECC_STICKY, NULL);
58625cf1a30Sjl 	}
58725cf1a30Sjl 
58825cf1a30Sjl 	for (i = 0; i < nflts; i++)
58925cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank;
59025cf1a30Sjl 
59125cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY,
59225cf1a30Sjl 		nflts, values, NULL);
59325cf1a30Sjl 
59425cf1a30Sjl 	for (i = 0; i < nflts; i++)
59525cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_cntl;
59625cf1a30Sjl 
59725cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY,
59825cf1a30Sjl 		nflts, values, NULL);
59925cf1a30Sjl 
60025cf1a30Sjl 	for (i = 0; i < nflts; i++)
60125cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_add;
60225cf1a30Sjl 
60325cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY,
60425cf1a30Sjl 		nflts, values, NULL);
60525cf1a30Sjl 
60625cf1a30Sjl 	for (i = 0; i < nflts; i++)
60725cf1a30Sjl 		values[i] = mc_aflt->mflt_stat[i]->mf_err_log;
60825cf1a30Sjl 
60925cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY,
61025cf1a30Sjl 		nflts, values, NULL);
61125cf1a30Sjl 
61225cf1a30Sjl 	for (i = 0; i < nflts; i++) {
61325cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
61425cf1a30Sjl 		if (flt_stat->mf_errlog_valid) {
61525cf1a30Sjl 			synd[i] = flt_stat->mf_synd;
61625cf1a30Sjl 			dslot[i] = flt_stat->mf_dimm_slot;
61725cf1a30Sjl 			values[i] = flt_stat->mf_dram_place;
61825cf1a30Sjl 		} else {
61925cf1a30Sjl 			synd[i] = 0;
62025cf1a30Sjl 			dslot[i] = 0;
62125cf1a30Sjl 			values[i] = 0;
62225cf1a30Sjl 		}
62325cf1a30Sjl 	}
62425cf1a30Sjl 
62525cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_SYND,
62625cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, synd, NULL);
62725cf1a30Sjl 
62825cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT,
62925cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, dslot, NULL);
63025cf1a30Sjl 
63125cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_ERR_DRAM,
63225cf1a30Sjl 		DATA_TYPE_UINT32_ARRAY, nflts, values, NULL);
63325cf1a30Sjl 
63425cf1a30Sjl 	blen = MAXPATHLEN;
63525cf1a30Sjl 	device_path[0] = 0;
63625cf1a30Sjl 	p = &device_path[0];
63725cf1a30Sjl 
63825cf1a30Sjl 	for (i = 0; i < nflts; i++) {
63925cf1a30Sjl 		int bank = flt_stat->mf_flt_maddr.ma_bank;
64025cf1a30Sjl 		int psb = -1;
64125cf1a30Sjl 
64225cf1a30Sjl 		flt_stat = mc_aflt->mflt_stat[i];
64325cf1a30Sjl 		psb = mc_opl_get_physical_board(
64425cf1a30Sjl 		    flt_stat->mf_flt_maddr.ma_bd);
64525cf1a30Sjl 
64625cf1a30Sjl 		if (psb != -1) {
64725cf1a30Sjl 			snprintf(p, blen, "/CMU%d/B%d", psb, bank);
64825cf1a30Sjl 		} else {
64925cf1a30Sjl 			snprintf(p, blen, "/CMU/B%d", bank);
65025cf1a30Sjl 		}
65125cf1a30Sjl 
65225cf1a30Sjl 		if (flt_stat->mf_errlog_valid) {
65325cf1a30Sjl 			snprintf(p + strlen(p), blen, "/MEM%d%d%c",
65425cf1a30Sjl 			    bank/2, (bank & 0x1) * 2 + dslot[i] & 1,
65525cf1a30Sjl 			    (dslot[i] & 0x2) ? 'B' : 'A');
65625cf1a30Sjl 		}
65725cf1a30Sjl 
65825cf1a30Sjl 		n = strlen(&device_path[0]);
65925cf1a30Sjl 		blen = MAXPATHLEN - n;
66025cf1a30Sjl 		p = &device_path[n];
66125cf1a30Sjl 		if (i < (nflts - 1)) {
66225cf1a30Sjl 			snprintf(p, blen, " ");
66325cf1a30Sjl 			n += 1; blen -= 1; p += 1;
66425cf1a30Sjl 		}
66525cf1a30Sjl 	}
66625cf1a30Sjl 
66725cf1a30Sjl 	/*
66825cf1a30Sjl 	 * UNUM format /LSB#/B#/MEMxyZ
66925cf1a30Sjl 	 * where x is the MAC# = Bank#/2
67025cf1a30Sjl 	 * y is slot info = (Bank# & 0x1)*2 + {0, 1} 0 for DIMM-L, 1 for DIMM-H
67125cf1a30Sjl 	 * DIMM-L is 0 in bit 13, DIMM-H is 1 in bit 13.
67225cf1a30Sjl 	 * Z is A(CS0) or B(CS1) given by bit 14
67325cf1a30Sjl 	 */
67425cf1a30Sjl 	(void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION,
67525cf1a30Sjl 		NULL, device_path, NULL, 0);
67625cf1a30Sjl 
67725cf1a30Sjl 	fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST,
67825cf1a30Sjl 		resource, NULL);
67925cf1a30Sjl 
68025cf1a30Sjl 	if (panicstr) {
68125cf1a30Sjl 		errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC);
68225cf1a30Sjl 	} else {
68325cf1a30Sjl 		(void) fm_ereport_post(ereport, EVCH_TRYHARD);
68425cf1a30Sjl 		fm_nvlist_destroy(ereport, FM_NVA_FREE);
68525cf1a30Sjl 		fm_nvlist_destroy(detector, FM_NVA_FREE);
68625cf1a30Sjl 		fm_nvlist_destroy(resource, FM_NVA_FREE);
68725cf1a30Sjl 	}
68825cf1a30Sjl }
68925cf1a30Sjl 
69025cf1a30Sjl static void
69125cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt)
69225cf1a30Sjl {
69325cf1a30Sjl 	int rv;
69425cf1a30Sjl 	page_t *pp;
69525cf1a30Sjl 	uint64_t errors;
69625cf1a30Sjl 	uint64_t pa = (uint64_t)(-1);
69725cf1a30Sjl 
69825cf1a30Sjl 	MC_LOG("mc_err_drain: %s\n",
69925cf1a30Sjl 		mc_aflt->mflt_erpt_class);
70025cf1a30Sjl 	/*
70125cf1a30Sjl 	 * we come here only when we have:
70225cf1a30Sjl 	 * In mirror mode: CMPE, MUE, SUE
70325cf1a30Sjl 	 * In normal mode: UE, Permanent CE
70425cf1a30Sjl 	 */
70525cf1a30Sjl 	rv = mcaddr_to_pa(mc_aflt->mflt_mcp,
70625cf1a30Sjl 		&(mc_aflt->mflt_stat[0]->mf_flt_maddr), &pa);
70725cf1a30Sjl 	if (rv == 0)
70825cf1a30Sjl 		mc_aflt->mflt_stat[0]->mf_flt_paddr = pa;
70925cf1a30Sjl 	else
71025cf1a30Sjl 		mc_aflt->mflt_stat[0]->mf_flt_paddr = (uint64_t)-1;
71125cf1a30Sjl 	if (rv == 0) {
71225cf1a30Sjl 		MC_LOG("mc_err_drain:pa = %lx\n", pa);
71325cf1a30Sjl 		pp = page_numtopp_nolock(pa >> PAGESHIFT);
71425cf1a30Sjl 
71525cf1a30Sjl 		if (pp) {
71625cf1a30Sjl 			/*
71725cf1a30Sjl 			 * Don't keep retiring and make ereports
71825cf1a30Sjl 			 * on bad pages in PTRL case
71925cf1a30Sjl 			 */
72025cf1a30Sjl 			MC_LOG("mc_err_drain:pp = %p\n", pp);
72125cf1a30Sjl 			if (mc_aflt->mflt_is_ptrl) {
72225cf1a30Sjl 				errors = 0;
72325cf1a30Sjl 				if (page_retire_check(pa, &errors) == 0) {
72425cf1a30Sjl 					MC_LOG("Page retired\n");
72525cf1a30Sjl 					return;
72625cf1a30Sjl 				}
72725cf1a30Sjl 				if (errors & mc_aflt->mflt_pr) {
72825cf1a30Sjl 					MC_LOG("errors %lx, mflt_pr %x\n",
72925cf1a30Sjl 						errors, mc_aflt->mflt_pr);
73025cf1a30Sjl 					return;
73125cf1a30Sjl 				}
73225cf1a30Sjl 			}
73325cf1a30Sjl 			MC_LOG("offline page %p error %x\n", pp,
73425cf1a30Sjl 				mc_aflt->mflt_pr);
73525cf1a30Sjl 			(void) page_retire(pa, mc_aflt->mflt_pr);
73625cf1a30Sjl 		}
73725cf1a30Sjl 	}
73825cf1a30Sjl 	mc_ereport_post(mc_aflt);
73925cf1a30Sjl }
74025cf1a30Sjl 
74125cf1a30Sjl #define	DIMM_SIZE 0x80000000
74225cf1a30Sjl 
74325cf1a30Sjl #define	INC_DIMM_ADDR(p, n) \
74425cf1a30Sjl 	(p)->ma_dimm_addr += n; \
74525cf1a30Sjl 	(p)->ma_dimm_addr &= (DIMM_SIZE - 1)
74625cf1a30Sjl 
74725cf1a30Sjl /*
74825cf1a30Sjl  * The restart address is actually defined in unit of PA[37:6]
74925cf1a30Sjl  * the mac patrol will convert that to dimm offset.  If the
75025cf1a30Sjl  * address is not in the bank, it will continue to search for
75125cf1a30Sjl  * the next PA that is within the bank.
75225cf1a30Sjl  *
75325cf1a30Sjl  * Also the mac patrol scans the dimms based on PA, not
75425cf1a30Sjl  * dimm offset.
75525cf1a30Sjl  */
75625cf1a30Sjl 
75725cf1a30Sjl static int
75825cf1a30Sjl restart_patrol(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr_info)
75925cf1a30Sjl {
76025cf1a30Sjl 	page_t *pp;
76125cf1a30Sjl 	uint32_t reg;
76225cf1a30Sjl 	uint64_t pa;
76325cf1a30Sjl 	int rv;
76425cf1a30Sjl 	int loop_count = 0;
76525cf1a30Sjl 
76625cf1a30Sjl 	reg = ldphysio(MAC_PTRL_CNTL(mcp, bank));
76725cf1a30Sjl 
76825cf1a30Sjl 	/* already running, so we just return */
76925cf1a30Sjl 	if (reg & MAC_CNTL_PTRL_START)
77025cf1a30Sjl 		return (0);
77125cf1a30Sjl 
77225cf1a30Sjl 	if (maddr_info == NULL || (maddr_info->mi_valid == 0)) {
77325cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
77425cf1a30Sjl 		return (0);
77525cf1a30Sjl 	}
77625cf1a30Sjl 
77725cf1a30Sjl 
77825cf1a30Sjl 	rv = mcaddr_to_pa(mcp, &maddr_info->mi_maddr, &pa);
77925cf1a30Sjl 	if (rv != 0) {
78025cf1a30Sjl 		MC_LOG("cannot convert mcaddr to pa. use auto restart\n");
78125cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
78225cf1a30Sjl 		return (0);
78325cf1a30Sjl 	}
78425cf1a30Sjl 
78525cf1a30Sjl 	/*
78625cf1a30Sjl 	 * pa is the last address scanned by the mac patrol
78725cf1a30Sjl 	 * we  calculate the next restart address as follows:
78825cf1a30Sjl 	 * first we always advance it by 64 byte. Then begin the loop.
78925cf1a30Sjl 	 * loop {
79025cf1a30Sjl 	 * if it is not in phys_install, we advance to next 64 MB boundary
79125cf1a30Sjl 	 * if it is not backed by a page structure, done
79225cf1a30Sjl 	 * if the page is bad, advance to the next page boundary.
79325cf1a30Sjl 	 * else done
79425cf1a30Sjl 	 * if the new address exceeds the board, wrap around.
79525cf1a30Sjl 	 * } <stop if we come back to the same page>
79625cf1a30Sjl 	 */
79725cf1a30Sjl 
79825cf1a30Sjl 	if (pa < mcp->mc_start_address || pa >= (mcp->mc_start_address
79925cf1a30Sjl 		+ mcp->mc_size)) {
80025cf1a30Sjl 		/* pa is not on this board, just retry */
80125cf1a30Sjl 		cmn_err(CE_WARN, "restart_patrol: invalid address %lx "
80225cf1a30Sjl 			"on board %d\n", pa, mcp->mc_board_num);
80325cf1a30Sjl 		MAC_PTRL_START(mcp, bank);
80425cf1a30Sjl 		return (0);
80525cf1a30Sjl 	}
80625cf1a30Sjl 
80725cf1a30Sjl 	MC_LOG("restart_patrol: pa = %lx\n", pa);
80825cf1a30Sjl 	if (maddr_info->mi_advance) {
80925cf1a30Sjl 		uint64_t new_pa;
81025cf1a30Sjl 
81125cf1a30Sjl 		if (IS_MIRROR(mcp, bank))
81225cf1a30Sjl 			new_pa = pa + 64 * 2;
81325cf1a30Sjl 		else
81425cf1a30Sjl 			new_pa = pa + 64;
81525cf1a30Sjl 
81625cf1a30Sjl 		if (!mc_valid_pa(mcp, new_pa)) {
81725cf1a30Sjl 			/* Isolation unit size is 64 MB */
81825cf1a30Sjl #define	MC_ISOLATION_BSIZE	(64 * 1024 * 1024)
81925cf1a30Sjl 			MC_LOG("Invalid PA\n");
82025cf1a30Sjl 			pa = roundup(new_pa + 1, MC_ISOLATION_BSIZE);
82125cf1a30Sjl 		} else {
82225cf1a30Sjl 			pp = page_numtopp_nolock(new_pa >> PAGESHIFT);
82325cf1a30Sjl 			if (pp != NULL) {
82425cf1a30Sjl 				uint64_t errors = 0;
82525cf1a30Sjl 				if (page_retire_check(new_pa, &errors) &&
82625cf1a30Sjl 					(errors == 0)) {
82725cf1a30Sjl 					MC_LOG("Page has no error\n");
82825cf1a30Sjl 					MAC_PTRL_START(mcp, bank);
82925cf1a30Sjl 					return (0);
83025cf1a30Sjl 				}
83125cf1a30Sjl 				/*
83225cf1a30Sjl 				 * skip bad pages
83325cf1a30Sjl 				 * and let the following loop to take care
83425cf1a30Sjl 				 */
83525cf1a30Sjl 				pa = roundup(new_pa + 1, PAGESIZE);
83625cf1a30Sjl 				MC_LOG("Skipping bad page to %lx\n", pa);
83725cf1a30Sjl 			} else {
83825cf1a30Sjl 				MC_LOG("Page has no page structure\n");
83925cf1a30Sjl 				MAC_PTRL_START(mcp, bank);
84025cf1a30Sjl 				return (0);
84125cf1a30Sjl 			}
84225cf1a30Sjl 		}
84325cf1a30Sjl 	}
84425cf1a30Sjl 
84525cf1a30Sjl 	/*
84625cf1a30Sjl 	 * if we wrap around twice, we just give up and let
84725cf1a30Sjl 	 * mac patrol decide.
84825cf1a30Sjl 	 */
84925cf1a30Sjl 	MC_LOG("pa is now %lx\n", pa);
85025cf1a30Sjl 	while (loop_count <= 1) {
85125cf1a30Sjl 		if (!mc_valid_pa(mcp, pa)) {
85225cf1a30Sjl 			MC_LOG("pa is not valid. round up to 64 MB\n");
85325cf1a30Sjl 			pa = roundup(pa + 1, 64 * 1024 * 1024);
85425cf1a30Sjl 		} else {
85525cf1a30Sjl 			pp = page_numtopp_nolock(pa >> PAGESHIFT);
85625cf1a30Sjl 			if (pp != NULL) {
85725cf1a30Sjl 				uint64_t errors = 0;
85825cf1a30Sjl 				if (page_retire_check(pa, &errors) &&
85925cf1a30Sjl 					(errors == 0)) {
86025cf1a30Sjl 					MC_LOG("Page has no error\n");
86125cf1a30Sjl 					break;
86225cf1a30Sjl 				}
86325cf1a30Sjl 				/* skip bad pages */
86425cf1a30Sjl 				pa = roundup(pa + 1, PAGESIZE);
86525cf1a30Sjl 				MC_LOG("Skipping bad page to %lx\n", pa);
86625cf1a30Sjl 			} else {
86725cf1a30Sjl 				MC_LOG("Page has no page structure\n");
86825cf1a30Sjl 				break;
86925cf1a30Sjl 			}
87025cf1a30Sjl 		}
87125cf1a30Sjl 		if (pa >= (mcp->mc_start_address + mcp->mc_size)) {
87225cf1a30Sjl 			MC_LOG("Wrap around\n");
87325cf1a30Sjl 			pa = mcp->mc_start_address;
87425cf1a30Sjl 			loop_count++;
87525cf1a30Sjl 		}
87625cf1a30Sjl 	}
87725cf1a30Sjl 
87825cf1a30Sjl 	/* retstart MAC patrol: PA[37:6] */
87925cf1a30Sjl 	MC_LOG("restart at pa = %lx\n", pa);
88025cf1a30Sjl 	ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa));
88125cf1a30Sjl 	MAC_PTRL_START_ADD(mcp, bank);
88225cf1a30Sjl 
88325cf1a30Sjl 	return (0);
88425cf1a30Sjl }
88525cf1a30Sjl 
88625cf1a30Sjl /*
88725cf1a30Sjl  * Rewriting is used for two purposes.
88825cf1a30Sjl  *  - to correct the error in memory.
88925cf1a30Sjl  *  - to determine whether the error is permanent or intermittent.
89025cf1a30Sjl  * It's done by writing the address in MAC_BANKm_REWRITE_ADD
89125cf1a30Sjl  * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that,
89225cf1a30Sjl  * REW_END (and REW_CE/REW_UE if some error detected) is set when
89325cf1a30Sjl  * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM.
89425cf1a30Sjl  *
89525cf1a30Sjl  * Note that rewrite operation doesn't change RAW_UE to Marked UE.
89625cf1a30Sjl  * Therefore, we use it only CE case.
89725cf1a30Sjl  */
89825cf1a30Sjl static uint32_t
89925cf1a30Sjl do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr)
90025cf1a30Sjl {
90125cf1a30Sjl 	uint32_t cntl;
90225cf1a30Sjl 	int count = 0;
90325cf1a30Sjl 
90425cf1a30Sjl 	/* first wait to make sure PTRL_STATUS is 0 */
90525cf1a30Sjl 	while (count++ < MAX_MC_LOOP_COUNT) {
90625cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
90725cf1a30Sjl 		if (!(cntl & MAC_CNTL_PTRL_STATUS))
90825cf1a30Sjl 			break;
90925cf1a30Sjl 		delay(drv_usectohz(10 * 1000));	/* 10 m.s. */
91025cf1a30Sjl 	}
91125cf1a30Sjl 	if (count >= MAX_MC_LOOP_COUNT)
91225cf1a30Sjl 		goto bad;
91325cf1a30Sjl 
91425cf1a30Sjl 	count = 0;
91525cf1a30Sjl 
91625cf1a30Sjl 	ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr);
91725cf1a30Sjl 	MAC_REW_REQ(mcp, bank);
91825cf1a30Sjl 
91925cf1a30Sjl 	do {
92025cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
92125cf1a30Sjl 		if (count++ >= MAX_MC_LOOP_COUNT) {
92225cf1a30Sjl 			goto bad;
92325cf1a30Sjl 		} else
92425cf1a30Sjl 			delay(drv_usectohz(10 * 1000));	/* 10 m.s. */
92525cf1a30Sjl 	/*
92625cf1a30Sjl 	 * If there are other MEMORY or PCI activities, this
92725cf1a30Sjl 	 * will be BUSY, else it should be set immediately
92825cf1a30Sjl 	 */
92925cf1a30Sjl 	} while (!(cntl & MAC_CNTL_REW_END));
93025cf1a30Sjl 
93125cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
93225cf1a30Sjl 	return (cntl);
93325cf1a30Sjl bad:
93425cf1a30Sjl 	/* This is bad.  Just reset the circuit */
93525cf1a30Sjl 	cmn_err(CE_WARN, "mc-opl rewrite timeout on /LSB%d/B%d\n",
93625cf1a30Sjl 		mcp->mc_board_num, bank);
93725cf1a30Sjl 	cntl = MAC_CNTL_REW_END;
93825cf1a30Sjl 	MAC_CMD(mcp, bank, MAC_CNTL_PTRL_RESET);
93925cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS);
94025cf1a30Sjl 	return (cntl);
94125cf1a30Sjl }
94225cf1a30Sjl 
94325cf1a30Sjl void
94425cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp)
94525cf1a30Sjl {
94625cf1a30Sjl 	int count = 0;
94725cf1a30Sjl 	scf_log_t *p;
94825cf1a30Sjl 	int bank;
94925cf1a30Sjl 
95025cf1a30Sjl 	while ((p = mcp->mc_scf_log) != NULL) {
95125cf1a30Sjl 		bank = p->sl_bank;
95225cf1a30Sjl 		while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank))
95325cf1a30Sjl 			& MAC_STATIC_ERR_VLD)) {
95425cf1a30Sjl 			if (count++ >= (MAX_MC_LOOP_COUNT)) {
95525cf1a30Sjl 				break;
95625cf1a30Sjl 			}
95725cf1a30Sjl 			delay(drv_usectohz(10 * 1000));	/* 10 m.s. */
95825cf1a30Sjl 		}
95925cf1a30Sjl 
96025cf1a30Sjl 		if (count < MAX_MC_LOOP_COUNT) {
96125cf1a30Sjl 			ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank),
96225cf1a30Sjl 				p->sl_err_log);
96325cf1a30Sjl 
96425cf1a30Sjl 			ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank),
96525cf1a30Sjl 				p->sl_err_add|MAC_STATIC_ERR_VLD);
96625cf1a30Sjl 			mcp->mc_scf_retry[bank] = 0;
96725cf1a30Sjl 		} else {
96825cf1a30Sjl 			/* if we try too many times, just drop the req */
96925cf1a30Sjl 			if (mcp->mc_scf_retry[bank]++ <= MAX_SCF_RETRY) {
97025cf1a30Sjl 				return;
97125cf1a30Sjl 			} else {
97225cf1a30Sjl 				cmn_err(CE_WARN, "SCF is not responding. "
97325cf1a30Sjl 					"Dropping the SCF LOG\n");
97425cf1a30Sjl 			}
97525cf1a30Sjl 		}
97625cf1a30Sjl 		mcp->mc_scf_log = p->sl_next;
97725cf1a30Sjl 		mcp->mc_scf_total--;
97825cf1a30Sjl 		ASSERT(mcp->mc_scf_total >= 0);
97925cf1a30Sjl 		kmem_free(p, sizeof (scf_log_t));
98025cf1a30Sjl 	}
98125cf1a30Sjl }
98225cf1a30Sjl 
98325cf1a30Sjl void
98425cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank)
98525cf1a30Sjl {
98625cf1a30Sjl 	scf_log_t *p;
98725cf1a30Sjl 
98825cf1a30Sjl 	if (mcp->mc_scf_total >= MAX_SCF_LOGS) {
98925cf1a30Sjl 		cmn_err(CE_WARN,
99025cf1a30Sjl 			"Max# SCF logs excceded on /LSB%d/B%d\n",
99125cf1a30Sjl 			mcp->mc_board_num, bank);
99225cf1a30Sjl 		return;
99325cf1a30Sjl 	}
99425cf1a30Sjl 	p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP);
99525cf1a30Sjl 	p->sl_next = 0;
99625cf1a30Sjl 	p->sl_err_add = flt_stat->mf_err_add;
99725cf1a30Sjl 	p->sl_err_log = flt_stat->mf_err_log;
99825cf1a30Sjl 	p->sl_bank = bank;
99925cf1a30Sjl 
100025cf1a30Sjl 	if (mcp->mc_scf_log == NULL) {
100125cf1a30Sjl 		/*
100225cf1a30Sjl 		 * we rely on mc_scf_log to detect NULL queue.
100325cf1a30Sjl 		 * mc_scf_log_tail is irrelevant is such case.
100425cf1a30Sjl 		 */
100525cf1a30Sjl 		mcp->mc_scf_log_tail = mcp->mc_scf_log = p;
100625cf1a30Sjl 	} else {
100725cf1a30Sjl 		mcp->mc_scf_log_tail->sl_next = p;
100825cf1a30Sjl 		mcp->mc_scf_log_tail = p;
100925cf1a30Sjl 	}
101025cf1a30Sjl 	mcp->mc_scf_total++;
101125cf1a30Sjl }
101225cf1a30Sjl 
101325cf1a30Sjl /*
101425cf1a30Sjl  * This routine determines what kind of CE happens, intermittent
101525cf1a30Sjl  * or permanent as follows. (See 4.7.3 in Columbus2 PRM.)
101625cf1a30Sjl  * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register.
101725cf1a30Sjl  * - If CE is still detected on the same address even after doing
101825cf1a30Sjl  *   rewrite operation twice, it is determined as permanent error.
101925cf1a30Sjl  * - If error is not detected anymore, it is determined as intermittent
102025cf1a30Sjl  *   error.
102125cf1a30Sjl  * - If UE is detected due to rewrite operation, it should be treated
102225cf1a30Sjl  *   as UE.
102325cf1a30Sjl  */
102425cf1a30Sjl 
102525cf1a30Sjl /* ARGSUSED */
102625cf1a30Sjl static void
102725cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error)
102825cf1a30Sjl {
102925cf1a30Sjl 	uint32_t cntl;
103025cf1a30Sjl 	int i;
103125cf1a30Sjl 
103225cf1a30Sjl 	flt_stat->mf_type = FLT_TYPE_PERMANENT_CE;
103325cf1a30Sjl 	/*
103425cf1a30Sjl 	 * rewrite request 1st time reads and correct error data
103525cf1a30Sjl 	 * and write to DIMM.  2nd rewrite request must be issued
103625cf1a30Sjl 	 * after REW_CE/UE/END is 0.  When the 2nd request is completed,
103725cf1a30Sjl 	 * if REW_CE = 1, then it is permanent CE.
103825cf1a30Sjl 	 */
103925cf1a30Sjl 	for (i = 0; i < 2; i++) {
104025cf1a30Sjl 		cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add);
104125cf1a30Sjl 		/*
104225cf1a30Sjl 		 * If the error becomes UE or CMPE
104325cf1a30Sjl 		 * we return to the caller immediately.
104425cf1a30Sjl 		 */
104525cf1a30Sjl 		if (cntl & MAC_CNTL_REW_UE) {
104625cf1a30Sjl 			if (ptrl_error)
104725cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE;
104825cf1a30Sjl 			else
104925cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_UE;
105025cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_UE;
105125cf1a30Sjl 			return;
105225cf1a30Sjl 		}
105325cf1a30Sjl 		if (cntl & MAC_CNTL_REW_CMPE) {
105425cf1a30Sjl 			if (ptrl_error)
105525cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE;
105625cf1a30Sjl 			else
105725cf1a30Sjl 				flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE;
105825cf1a30Sjl 			flt_stat->mf_type = FLT_TYPE_CMPE;
105925cf1a30Sjl 			return;
106025cf1a30Sjl 		}
106125cf1a30Sjl 	}
106225cf1a30Sjl 	if (!(cntl & MAC_CNTL_REW_CE)) {
106325cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE;
106425cf1a30Sjl 	}
106525cf1a30Sjl 
106625cf1a30Sjl 	if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
106725cf1a30Sjl 		/* report PERMANENT_CE to SP via SCF */
106825cf1a30Sjl 		if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) {
106925cf1a30Sjl 			mc_queue_scf_log(mcp, flt_stat, bank);
107025cf1a30Sjl 		}
107125cf1a30Sjl 	}
107225cf1a30Sjl }
107325cf1a30Sjl 
107425cf1a30Sjl #define	IS_CMPE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\
107525cf1a30Sjl 				MAC_CNTL_MI_CMPE))
107625cf1a30Sjl #define	IS_UE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE))
107725cf1a30Sjl #define	IS_CE(cntl, f)	((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE))
107825cf1a30Sjl #define	IS_OK(cntl, f)	(!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \
107925cf1a30Sjl 			MAC_CNTL_MI_ERRS)))
108025cf1a30Sjl 
108125cf1a30Sjl 
108225cf1a30Sjl static int
108325cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error)
108425cf1a30Sjl {
108525cf1a30Sjl 	if (ptrl_error) {
108625cf1a30Sjl 		return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE);
108725cf1a30Sjl 	} else {
108825cf1a30Sjl 		return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE);
108925cf1a30Sjl 	}
109025cf1a30Sjl }
109125cf1a30Sjl 
109225cf1a30Sjl void
109325cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value)
109425cf1a30Sjl {
109525cf1a30Sjl 	value |= mcp->mc_bank[bank].mcb_ptrl_cntl;
109625cf1a30Sjl 	ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value);
109725cf1a30Sjl }
109825cf1a30Sjl 
109925cf1a30Sjl static int
110025cf1a30Sjl mc_stop(mc_opl_t *mcp, int bank)
110125cf1a30Sjl {
110225cf1a30Sjl 	uint32_t reg;
110325cf1a30Sjl 	int count = 0;
110425cf1a30Sjl 
110525cf1a30Sjl 	reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
110625cf1a30Sjl 
110725cf1a30Sjl 	if (reg & MAC_CNTL_PTRL_START)
110825cf1a30Sjl 		MAC_PTRL_STOP(mcp, bank);
110925cf1a30Sjl 
111025cf1a30Sjl 	while (count++ <= MAX_MC_LOOP_COUNT) {
111125cf1a30Sjl 		reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
111225cf1a30Sjl 		if ((reg & MAC_CNTL_PTRL_STATUS) == 0)
111325cf1a30Sjl 			return (0);
111425cf1a30Sjl 		delay(drv_usectohz(10 * 1000));	/* 10 m.s. */
111525cf1a30Sjl 	}
111625cf1a30Sjl 	return (-1);
111725cf1a30Sjl }
111825cf1a30Sjl 
111925cf1a30Sjl static void
112025cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
112125cf1a30Sjl {
112225cf1a30Sjl 	flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
112325cf1a30Sjl 		MAC_CNTL_PTRL_ERRS;
112425cf1a30Sjl 	flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank));
112525cf1a30Sjl 	flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank));
112625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
112725cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
112825cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
112925cf1a30Sjl }
113025cf1a30Sjl 
113125cf1a30Sjl static void
113225cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat)
113325cf1a30Sjl {
113425cf1a30Sjl 	uint32_t status, old_status;
113525cf1a30Sjl 
113625cf1a30Sjl 	status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
113725cf1a30Sjl 		MAC_CNTL_MI_ERRS;
113825cf1a30Sjl 	old_status = 0;
113925cf1a30Sjl 
114025cf1a30Sjl 	/* we keep reading until the status is stable */
114125cf1a30Sjl 	while (old_status != status) {
114225cf1a30Sjl 		old_status = status;
114325cf1a30Sjl 		flt_stat->mf_err_add =
114425cf1a30Sjl 			LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank));
114525cf1a30Sjl 		flt_stat->mf_err_log =
114625cf1a30Sjl 			LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank));
114725cf1a30Sjl 		status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) &
114825cf1a30Sjl 			MAC_CNTL_MI_ERRS;
114925cf1a30Sjl 		if (status == old_status) {
115025cf1a30Sjl 			break;
115125cf1a30Sjl 		}
115225cf1a30Sjl 	}
115325cf1a30Sjl 
115425cf1a30Sjl 	flt_stat->mf_cntl = status;
115525cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num;
115625cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_bank = bank;
115725cf1a30Sjl 	flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add;
115825cf1a30Sjl }
115925cf1a30Sjl 
116025cf1a30Sjl 
116125cf1a30Sjl /*
116225cf1a30Sjl  * Error philosophy for mirror mode:
116325cf1a30Sjl  *
116425cf1a30Sjl  * PTRL (The error address for both banks are same, since ptrl stops if it
116525cf1a30Sjl  * detects error.)
116625cf1a30Sjl  * - Compaire error  Report CMPE.
116725cf1a30Sjl  *
116825cf1a30Sjl  * - UE-UE           Report MUE.  No rewrite.
116925cf1a30Sjl  *
117025cf1a30Sjl  * - UE-*	     UE-(CE/OK). Rewrite to scrub UE.  Report SUE.
117125cf1a30Sjl  *
117225cf1a30Sjl  * - CE-*            CE-(CE/OK). Scrub to determine if CE is permanent.
117325cf1a30Sjl  *                   If CE is permanent, inform SCF.  Once for each
117425cf1a30Sjl  *		     Dimm.  If CE becomes UE or CMPE, go back to above.
117525cf1a30Sjl  *
117625cf1a30Sjl  *
117725cf1a30Sjl  * MI (The error addresses for each bank are the same or different.)
117825cf1a30Sjl  * - Compair  error  If addresses are the same.  Just CMPE.
117925cf1a30Sjl  *		     If addresses are different (this could happen
118025cf1a30Sjl  *		     as a result of scrubbing.  Report each seperately.
118125cf1a30Sjl  *		     Only report error info on each side.
118225cf1a30Sjl  *
118325cf1a30Sjl  * - UE-UE           Addresses are the same.  Report MUE.
118425cf1a30Sjl  *		     Addresses are different.  Report SUE on each bank.
118525cf1a30Sjl  *		     Rewrite to clear UE.
118625cf1a30Sjl  *
118725cf1a30Sjl  * - UE-*	     UE-(CE/OK)
118825cf1a30Sjl  *		     Rewrite to clear UE.  Report SUE for the bank.
118925cf1a30Sjl  *
119025cf1a30Sjl  * - CE-*            CE-(CE/OK).  Scrub to determine if CE is permanent.
119125cf1a30Sjl  *                   If CE becomes UE or CMPE, go back to above.
119225cf1a30Sjl  *
119325cf1a30Sjl  */
119425cf1a30Sjl 
119525cf1a30Sjl static int
119625cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat)
119725cf1a30Sjl {
119825cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
119925cf1a30Sjl 	int i;
120025cf1a30Sjl 	int rv = 0;
120125cf1a30Sjl 
120225cf1a30Sjl 	MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n",
120325cf1a30Sjl 		flt_stat[0].mf_cntl, flt_stat[1].mf_cntl);
120425cf1a30Sjl 
120525cf1a30Sjl 	if (ptrl_error) {
120625cf1a30Sjl 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl)
120725cf1a30Sjl 			& MAC_CNTL_PTRL_ERRS) == 0)
120825cf1a30Sjl 			return (0);
120925cf1a30Sjl 	} else {
121025cf1a30Sjl 		if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl)
121125cf1a30Sjl 			& MAC_CNTL_MI_ERRS) == 0)
121225cf1a30Sjl 			return (0);
121325cf1a30Sjl 	}
121425cf1a30Sjl 
121525cf1a30Sjl 	/*
121625cf1a30Sjl 	 * First we take care of the case of CE
121725cf1a30Sjl 	 * because they can become UE or CMPE
121825cf1a30Sjl 	 */
121925cf1a30Sjl 	for (i = 0; i < 2; i++) {
122025cf1a30Sjl 		if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) {
122125cf1a30Sjl 			MC_LOG("CE detected on bank %d\n",
122225cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_bank);
122325cf1a30Sjl 			mc_scrub_ce(mcp, flt_stat[i].mf_flt_maddr.ma_bank,
122425cf1a30Sjl 				&flt_stat[i], ptrl_error);
122525cf1a30Sjl 			rv = 1;
122625cf1a30Sjl 		}
122725cf1a30Sjl 	}
122825cf1a30Sjl 
122925cf1a30Sjl 	/* The above scrubbing can turn CE into UE or CMPE */
123025cf1a30Sjl 
123125cf1a30Sjl 	/*
123225cf1a30Sjl 	 * Now we distinguish two cases: same address or not
123325cf1a30Sjl 	 * the same address.  It might seem more intuitive to
123425cf1a30Sjl 	 * distinguish PTRL v.s. MI error but it is more
123525cf1a30Sjl 	 * complicated that way.
123625cf1a30Sjl 	 */
123725cf1a30Sjl 
123825cf1a30Sjl 	if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) {
123925cf1a30Sjl 
124025cf1a30Sjl 		if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) ||
124125cf1a30Sjl 		    IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) {
124225cf1a30Sjl 			flt_stat[0].mf_type = FLT_TYPE_CMPE;
124325cf1a30Sjl 			flt_stat[1].mf_type = FLT_TYPE_CMPE;
124425cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
124525cf1a30Sjl 			MC_LOG("cmpe error detected\n");
124625cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
124725cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
124825cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
124925cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
125025cf1a30Sjl 			mc_err_drain(mc_aflt);
125125cf1a30Sjl 			return (1);
125225cf1a30Sjl 		}
125325cf1a30Sjl 
125425cf1a30Sjl 		if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) &&
125525cf1a30Sjl 			IS_UE(flt_stat[1].mf_cntl, ptrl_error)) {
125625cf1a30Sjl 			/* Both side are UE's */
125725cf1a30Sjl 
125825cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[0]);
125925cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[1]);
126025cf1a30Sjl 			MC_LOG("MUE detected\n");
126125cf1a30Sjl 			flt_stat[0].mf_type = flt_stat[1].mf_type =
126225cf1a30Sjl 				FLT_TYPE_MUE;
126325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_MUE;
126425cf1a30Sjl 			mc_aflt->mflt_nflts = 2;
126525cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[0];
126625cf1a30Sjl 			mc_aflt->mflt_stat[1] = &flt_stat[1];
126725cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
126825cf1a30Sjl 			mc_err_drain(mc_aflt);
126925cf1a30Sjl 			return (1);
127025cf1a30Sjl 		}
127125cf1a30Sjl 
127225cf1a30Sjl 		/* Now the only case is UE/CE, UE/OK, or don't care */
127325cf1a30Sjl 		for (i = 0; i < 2; i++) {
127425cf1a30Sjl 		    if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
127525cf1a30Sjl 			/* If we have CE, we would have done REW */
127625cf1a30Sjl 			if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) {
127725cf1a30Sjl 				(void) do_rewrite(mcp,
127825cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_bank,
127925cf1a30Sjl 				    flt_stat[i].mf_flt_maddr.ma_dimm_addr);
128025cf1a30Sjl 			}
128125cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
128225cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
128325cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
128425cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
128525cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
128625cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
128725cf1a30Sjl 			mc_err_drain(mc_aflt);
128825cf1a30Sjl 			/* Once we hit a UE/CE or UE/OK case, done */
128925cf1a30Sjl 			return (1);
129025cf1a30Sjl 		    }
129125cf1a30Sjl 		}
129225cf1a30Sjl 
129325cf1a30Sjl 	} else {
129425cf1a30Sjl 		/*
129525cf1a30Sjl 		 * addresses are different. That means errors
129625cf1a30Sjl 		 * on the 2 banks are not related at all.
129725cf1a30Sjl 		 */
129825cf1a30Sjl 		for (i = 0; i < 2; i++) {
129925cf1a30Sjl 		    if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) {
130025cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_CMPE;
130125cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CMPE;
130225cf1a30Sjl 			MC_LOG("cmpe error detected\n");
130325cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
130425cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
130525cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
130625cf1a30Sjl 			mc_err_drain(mc_aflt);
130725cf1a30Sjl 			/* no more report on this bank */
130825cf1a30Sjl 			flt_stat[i].mf_cntl = 0;
130925cf1a30Sjl 			rv = 1;
131025cf1a30Sjl 		    }
131125cf1a30Sjl 		}
131225cf1a30Sjl 
131325cf1a30Sjl 		for (i = 0; i < 2; i++) {
131425cf1a30Sjl 		    if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) {
131525cf1a30Sjl 			(void) do_rewrite(mcp,
131625cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_bank,
131725cf1a30Sjl 				flt_stat[i].mf_flt_maddr.ma_dimm_addr);
131825cf1a30Sjl 			flt_stat[i].mf_type = FLT_TYPE_UE;
131925cf1a30Sjl 			MAC_SET_ERRLOG_INFO(&flt_stat[i]);
132025cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_SUE;
132125cf1a30Sjl 			mc_aflt->mflt_stat[0] = &flt_stat[i];
132225cf1a30Sjl 			mc_aflt->mflt_nflts = 1;
132325cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
132425cf1a30Sjl 			mc_err_drain(mc_aflt);
132525cf1a30Sjl 			rv = 1;
132625cf1a30Sjl 		    }
132725cf1a30Sjl 		}
132825cf1a30Sjl 	}
132925cf1a30Sjl 	return (rv);
133025cf1a30Sjl }
133125cf1a30Sjl 
133225cf1a30Sjl static void
133325cf1a30Sjl mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr)
133425cf1a30Sjl {
133525cf1a30Sjl 	mc_aflt_t mc_aflt;
133625cf1a30Sjl 	mc_flt_stat_t flt_stat[2], mi_flt_stat[2];
133725cf1a30Sjl 	int other_bank;
133825cf1a30Sjl 
133925cf1a30Sjl 	if (mc_stop(mcp, bank)) {
134025cf1a30Sjl 		cmn_err(CE_WARN, "Cannot stop Memory Patrol at /LSB%d/B%d\n",
134125cf1a30Sjl 			mcp->mc_board_num, bank);
134225cf1a30Sjl 		return;
134325cf1a30Sjl 	}
134425cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
134525cf1a30Sjl 	bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t));
134625cf1a30Sjl 	bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t));
134725cf1a30Sjl 
134825cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
134925cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
135025cf1a30Sjl 
135125cf1a30Sjl 	/* Now read all the registers into flt_stat */
135225cf1a30Sjl 
135325cf1a30Sjl 	MC_LOG("Reading registers of bank %d\n", bank);
135425cf1a30Sjl 	/* patrol registers */
135525cf1a30Sjl 	mc_read_ptrl_reg(mcp, bank, &flt_stat[0]);
135625cf1a30Sjl 
135725cf1a30Sjl 	ASSERT(maddr);
135825cf1a30Sjl 	maddr->mi_maddr = flt_stat[0].mf_flt_maddr;
135925cf1a30Sjl 
136025cf1a30Sjl 	MC_LOG("ptrl registers cntl %x add %x log %x\n",
136125cf1a30Sjl 		flt_stat[0].mf_cntl,
136225cf1a30Sjl 		flt_stat[0].mf_err_add,
136325cf1a30Sjl 		flt_stat[0].mf_err_log);
136425cf1a30Sjl 
136525cf1a30Sjl 	/* MI registers */
136625cf1a30Sjl 	mc_read_mi_reg(mcp, bank, &mi_flt_stat[0]);
136725cf1a30Sjl 
136825cf1a30Sjl 	MC_LOG("MI registers cntl %x add %x log %x\n",
136925cf1a30Sjl 		mi_flt_stat[0].mf_cntl,
137025cf1a30Sjl 		mi_flt_stat[0].mf_err_add,
137125cf1a30Sjl 		mi_flt_stat[0].mf_err_log);
137225cf1a30Sjl 
137325cf1a30Sjl 	other_bank = bank^1;
137425cf1a30Sjl 
137525cf1a30Sjl 	MC_LOG("Reading registers of bank %d\n", other_bank);
137625cf1a30Sjl 
137725cf1a30Sjl 	ASSERT(mcp->mc_bank[other_bank].mcb_status & BANK_INSTALLED);
137825cf1a30Sjl 
137925cf1a30Sjl 	mc_read_ptrl_reg(mcp, other_bank, &flt_stat[1]);
138025cf1a30Sjl 	MC_LOG("ptrl registers cntl %x add %x log %x\n",
138125cf1a30Sjl 		flt_stat[1].mf_cntl,
138225cf1a30Sjl 		flt_stat[1].mf_err_add,
138325cf1a30Sjl 		flt_stat[1].mf_err_log);
138425cf1a30Sjl 
138525cf1a30Sjl 	/* MI registers */
138625cf1a30Sjl 	mc_read_mi_reg(mcp, other_bank, &mi_flt_stat[1]);
138725cf1a30Sjl 	MC_LOG("MI registers cntl %x add %x log %x\n",
138825cf1a30Sjl 		mi_flt_stat[1].mf_cntl,
138925cf1a30Sjl 		mi_flt_stat[1].mf_err_add,
139025cf1a30Sjl 		mi_flt_stat[1].mf_err_log);
139125cf1a30Sjl 
139225cf1a30Sjl 	/* clear errors once we read all the registers */
139325cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, other_bank,
139425cf1a30Sjl 		(MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
139525cf1a30Sjl 
139625cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
139725cf1a30Sjl 
139825cf1a30Sjl 	/* Process PTRL errors first */
139925cf1a30Sjl 
140025cf1a30Sjl 	/* if not error mode, cntl1 is 0 */
140125cf1a30Sjl 	if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) ||
140225cf1a30Sjl 		(flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID))
140325cf1a30Sjl 		flt_stat[0].mf_cntl = 0;
140425cf1a30Sjl 
140525cf1a30Sjl 	if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) ||
140625cf1a30Sjl 		(flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID))
140725cf1a30Sjl 		flt_stat[1].mf_cntl = 0;
140825cf1a30Sjl 
140925cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
141025cf1a30Sjl 	maddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]);
141125cf1a30Sjl 
141225cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 0;
141325cf1a30Sjl 	mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]);
141425cf1a30Sjl }
141525cf1a30Sjl 
141625cf1a30Sjl static int
141725cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt,
141825cf1a30Sjl 	mc_flt_stat_t *flt_stat)
141925cf1a30Sjl {
142025cf1a30Sjl 	int ptrl_error = mc_aflt->mflt_is_ptrl;
142125cf1a30Sjl 	int rv = 0;
142225cf1a30Sjl 
142325cf1a30Sjl 	mc_aflt->mflt_erpt_class = NULL;
142425cf1a30Sjl 	if (IS_UE(flt_stat->mf_cntl, ptrl_error)) {
142525cf1a30Sjl 		MC_LOG("UE deteceted\n");
142625cf1a30Sjl 		flt_stat->mf_type = FLT_TYPE_UE;
142725cf1a30Sjl 		mc_aflt->mflt_erpt_class = MC_OPL_UE;
142825cf1a30Sjl 		mc_aflt->mflt_pr = PR_UE;
142925cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
143025cf1a30Sjl 		rv = 1;
143125cf1a30Sjl 	} else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) {
143225cf1a30Sjl 		MC_LOG("CE deteceted\n");
143325cf1a30Sjl 		MAC_SET_ERRLOG_INFO(flt_stat);
143425cf1a30Sjl 
143525cf1a30Sjl 		/* Error type can change after scrubing */
143625cf1a30Sjl 		mc_scrub_ce(mcp, bank, flt_stat, ptrl_error);
143725cf1a30Sjl 
143825cf1a30Sjl 		if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) {
143925cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_CE;
144025cf1a30Sjl 			mc_aflt->mflt_pr = PR_MCE;
144125cf1a30Sjl 		} else if (flt_stat->mf_type == FLT_TYPE_UE) {
144225cf1a30Sjl 			mc_aflt->mflt_erpt_class = MC_OPL_UE;
144325cf1a30Sjl 			mc_aflt->mflt_pr = PR_UE;
144425cf1a30Sjl 		}
144525cf1a30Sjl 		rv = 1;
144625cf1a30Sjl 	}
144725cf1a30Sjl 	MC_LOG("mc_process_error: fault type %x erpt %s\n",
144825cf1a30Sjl 		flt_stat->mf_type,
144925cf1a30Sjl 		mc_aflt->mflt_erpt_class);
145025cf1a30Sjl 	if (mc_aflt->mflt_erpt_class) {
145125cf1a30Sjl 		mc_aflt->mflt_stat[0] = flt_stat;
145225cf1a30Sjl 		mc_aflt->mflt_nflts = 1;
145325cf1a30Sjl 		mc_err_drain(mc_aflt);
145425cf1a30Sjl 	}
145525cf1a30Sjl 	return (rv);
145625cf1a30Sjl }
145725cf1a30Sjl 
145825cf1a30Sjl static void
145925cf1a30Sjl mc_error_handler(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr)
146025cf1a30Sjl {
146125cf1a30Sjl 	mc_aflt_t mc_aflt;
146225cf1a30Sjl 	mc_flt_stat_t flt_stat, mi_flt_stat;
146325cf1a30Sjl 
146425cf1a30Sjl 	if (mc_stop(mcp, bank)) {
146525cf1a30Sjl 		cmn_err(CE_WARN, "Cannot stop Memory Patrol at /LSB%d/B%d\n",
146625cf1a30Sjl 			mcp->mc_board_num, bank);
146725cf1a30Sjl 		return;
146825cf1a30Sjl 	}
146925cf1a30Sjl 
147025cf1a30Sjl 	bzero(&mc_aflt, sizeof (mc_aflt_t));
147125cf1a30Sjl 	bzero(&flt_stat, sizeof (mc_flt_stat_t));
147225cf1a30Sjl 	bzero(&mi_flt_stat, sizeof (mc_flt_stat_t));
147325cf1a30Sjl 
147425cf1a30Sjl 	mc_aflt.mflt_mcp = mcp;
147525cf1a30Sjl 	mc_aflt.mflt_id = gethrtime();
147625cf1a30Sjl 
147725cf1a30Sjl 	/* patrol registers */
147825cf1a30Sjl 	mc_read_ptrl_reg(mcp, bank, &flt_stat);
147925cf1a30Sjl 
148025cf1a30Sjl 	ASSERT(maddr);
148125cf1a30Sjl 	maddr->mi_maddr = flt_stat.mf_flt_maddr;
148225cf1a30Sjl 
148325cf1a30Sjl 	MC_LOG("ptrl registers cntl %x add %x log %x\n",
148425cf1a30Sjl 		flt_stat.mf_cntl,
148525cf1a30Sjl 		flt_stat.mf_err_add,
148625cf1a30Sjl 		flt_stat.mf_err_log);
148725cf1a30Sjl 
148825cf1a30Sjl 	/* MI registers */
148925cf1a30Sjl 	mc_read_mi_reg(mcp, bank, &mi_flt_stat);
149025cf1a30Sjl 
149125cf1a30Sjl 	MC_LOG("MI registers cntl %x add %x log %x\n",
149225cf1a30Sjl 		mi_flt_stat.mf_cntl,
149325cf1a30Sjl 		mi_flt_stat.mf_err_add,
149425cf1a30Sjl 		mi_flt_stat.mf_err_log);
149525cf1a30Sjl 
149625cf1a30Sjl 	/* clear errors once we read all the registers */
149725cf1a30Sjl 	MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS));
149825cf1a30Sjl 
149925cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 1;
150025cf1a30Sjl 	if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) &&
150125cf1a30Sjl 		((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
150225cf1a30Sjl 		((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
150325cf1a30Sjl 		maddr->mi_valid = mc_process_error(mcp, bank,
150425cf1a30Sjl 			&mc_aflt, &flt_stat);
150525cf1a30Sjl 	}
150625cf1a30Sjl 	mc_aflt.mflt_is_ptrl = 0;
150725cf1a30Sjl 	if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) &&
150825cf1a30Sjl 		((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) &&
150925cf1a30Sjl 		((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) {
151025cf1a30Sjl 		mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat);
151125cf1a30Sjl 	}
151225cf1a30Sjl }
151325cf1a30Sjl 
151425cf1a30Sjl /*
151525cf1a30Sjl  *	memory patrol error handling algorithm:
151625cf1a30Sjl  *	timeout() is used to do periodic polling
151725cf1a30Sjl  *	This is the flow chart.
151825cf1a30Sjl  *	timeout ->
151925cf1a30Sjl  *	mc_check_errors()
152025cf1a30Sjl  *	    if memory bank is installed, read the status register
152125cf1a30Sjl  *	    if any error bit is set,
152225cf1a30Sjl  *	    -> mc_error_handler()
152325cf1a30Sjl  *	        -> mc_stop()
152425cf1a30Sjl  *		-> read all error regsiters
152525cf1a30Sjl  *	        -> mc_process_error()
152625cf1a30Sjl  *	            determine error type
152725cf1a30Sjl  *	            rewrite to clear error or scrub to determine CE type
152825cf1a30Sjl  *	            inform SCF on permanent CE
152925cf1a30Sjl  *	        -> mc_err_drain
153025cf1a30Sjl  *	            page offline processing
153125cf1a30Sjl  *	            -> mc_ereport_post()
153225cf1a30Sjl  */
153325cf1a30Sjl 
153425cf1a30Sjl static void
153525cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp)
153625cf1a30Sjl {
153725cf1a30Sjl 	mc_addr_info_t maddr_info;
153825cf1a30Sjl 	int i, error_count = 0;
153925cf1a30Sjl 	uint32_t stat, cntl;
154025cf1a30Sjl 
154125cf1a30Sjl 	/*
154225cf1a30Sjl 	 * scan errors.
154325cf1a30Sjl 	 */
154425cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
154525cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
154625cf1a30Sjl 			stat = ldphysio(MAC_PTRL_STAT(mcp, i));
154725cf1a30Sjl 			cntl = ldphysio(MAC_PTRL_CNTL(mcp, i));
154825cf1a30Sjl 			if (cntl & MAC_CNTL_PTRL_ADD_MAX) {
154925cf1a30Sjl 				mcp->mc_period++;
155025cf1a30Sjl 				MC_LOG("mc period %ld on "
155125cf1a30Sjl 				    "/LSB%d/B%d\n", mcp->mc_period,
155225cf1a30Sjl 				    mcp->mc_board_num, i);
155325cf1a30Sjl 				MAC_CLEAR_MAX(mcp, i);
155425cf1a30Sjl 			}
155525cf1a30Sjl 			if (mc_debug_show_all) {
155625cf1a30Sjl 				MC_LOG("/LSB%d/B%d stat %x cntl %x\n",
155725cf1a30Sjl 					mcp->mc_board_num, i,
155825cf1a30Sjl 					stat, cntl);
155925cf1a30Sjl 			}
156025cf1a30Sjl 			if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) {
156125cf1a30Sjl 				maddr_info.mi_valid = 0;
156225cf1a30Sjl 				maddr_info.mi_advance = 1;
156325cf1a30Sjl 				if (IS_MIRROR(mcp, i))
156425cf1a30Sjl 					mc_error_handler_mir(mcp, i,
156525cf1a30Sjl 						&maddr_info);
156625cf1a30Sjl 				else
156725cf1a30Sjl 					mc_error_handler(mcp, i, &maddr_info);
156825cf1a30Sjl 
156925cf1a30Sjl 				error_count++;
157025cf1a30Sjl 				restart_patrol(mcp, i, &maddr_info);
157125cf1a30Sjl 			} else {
157225cf1a30Sjl 				restart_patrol(mcp, i, NULL);
157325cf1a30Sjl 			}
157425cf1a30Sjl 		}
157525cf1a30Sjl 	}
157625cf1a30Sjl 	mc_process_scf_log(mcp);
157725cf1a30Sjl 	if (error_count > 0)
157825cf1a30Sjl 		mcp->mc_last_error += error_count;
157925cf1a30Sjl 	else
158025cf1a30Sjl 		mcp->mc_last_error = 0;
158125cf1a30Sjl }
158225cf1a30Sjl 
158325cf1a30Sjl /* this is just a wrapper for the above func */
158425cf1a30Sjl 
158525cf1a30Sjl static void
158625cf1a30Sjl mc_check_errors(void *arg)
158725cf1a30Sjl {
158825cf1a30Sjl 	mc_opl_t *mcp = (mc_opl_t *)arg;
158925cf1a30Sjl 	clock_t interval;
159025cf1a30Sjl 
159125cf1a30Sjl 	/*
159225cf1a30Sjl 	 * scan errors.
159325cf1a30Sjl 	 */
159425cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
159525cf1a30Sjl 	mcp->mc_tid = 0;
159625cf1a30Sjl 	if ((mcp->mc_status & MC_POLL_RUNNING) &&
159725cf1a30Sjl 		!(mcp->mc_status & MC_SOFT_SUSPENDED)) {
159825cf1a30Sjl 		mc_check_errors_func(mcp);
159925cf1a30Sjl 
160025cf1a30Sjl 		if (mcp->mc_last_error > 0) {
160125cf1a30Sjl 			interval = (mcp->mc_interval_hz) >> mcp->mc_last_error;
160225cf1a30Sjl 			if (interval < 1)
160325cf1a30Sjl 				interval = 1;
160425cf1a30Sjl 		} else
160525cf1a30Sjl 			interval = mcp->mc_interval_hz;
160625cf1a30Sjl 
160725cf1a30Sjl 		mcp->mc_tid = timeout(mc_check_errors, mcp,
160825cf1a30Sjl 		    interval);
160925cf1a30Sjl 	}
161025cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
161125cf1a30Sjl }
161225cf1a30Sjl 
161325cf1a30Sjl static void
161425cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr)
161525cf1a30Sjl {
161625cf1a30Sjl 	maddr->ma_bd = mcp->mc_board_num;
161725cf1a30Sjl 	maddr->ma_bank = bank;
161825cf1a30Sjl 	maddr->ma_dimm_addr = 0;
161925cf1a30Sjl }
162025cf1a30Sjl 
162125cf1a30Sjl typedef struct mc_mem_range {
162225cf1a30Sjl 	uint64_t	addr;
162325cf1a30Sjl 	uint64_t	size;
162425cf1a30Sjl } mc_mem_range_t;
162525cf1a30Sjl 
162625cf1a30Sjl static int
162725cf1a30Sjl get_base_address(mc_opl_t *mcp)
162825cf1a30Sjl {
162925cf1a30Sjl 	mc_mem_range_t *mem_range;
163025cf1a30Sjl 	int len;
163125cf1a30Sjl 
163225cf1a30Sjl 	if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
163325cf1a30Sjl 		"sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) {
163425cf1a30Sjl 		return (DDI_FAILURE);
163525cf1a30Sjl 	}
163625cf1a30Sjl 
163725cf1a30Sjl 	mcp->mc_start_address = mem_range->addr;
163825cf1a30Sjl 	mcp->mc_size = mem_range->size;
163925cf1a30Sjl 
164025cf1a30Sjl 	kmem_free(mem_range, len);
164125cf1a30Sjl 	return (DDI_SUCCESS);
164225cf1a30Sjl }
164325cf1a30Sjl 
164425cf1a30Sjl struct mc_addr_spec {
164525cf1a30Sjl 	uint32_t bank;
164625cf1a30Sjl 	uint32_t phys_hi;
164725cf1a30Sjl 	uint32_t phys_lo;
164825cf1a30Sjl };
164925cf1a30Sjl 
165025cf1a30Sjl #define	REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo)
165125cf1a30Sjl 
165225cf1a30Sjl static char *mc_tbl_name[] = {
165325cf1a30Sjl 	"cs0-mc-pa-trans-table",
165425cf1a30Sjl 	"cs1-mc-pa-trans-table"
165525cf1a30Sjl };
165625cf1a30Sjl 
165725cf1a30Sjl static int
165825cf1a30Sjl mc_valid_pa(mc_opl_t *mcp, uint64_t pa)
165925cf1a30Sjl {
166025cf1a30Sjl 	struct memlist *ml;
166125cf1a30Sjl 
166225cf1a30Sjl 	if (mcp->mlist == NULL)
166325cf1a30Sjl 		mc_get_mlist(mcp);
166425cf1a30Sjl 
166525cf1a30Sjl 	for (ml = mcp->mlist; ml; ml = ml->next) {
166625cf1a30Sjl 		if (ml->address <= pa && pa < (ml->address + ml->size))
166725cf1a30Sjl 			return (1);
166825cf1a30Sjl 	}
166925cf1a30Sjl 	return (0);
167025cf1a30Sjl }
167125cf1a30Sjl 
167225cf1a30Sjl static void
167325cf1a30Sjl mc_memlist_delete(struct memlist *mlist)
167425cf1a30Sjl {
167525cf1a30Sjl 	struct memlist *ml;
167625cf1a30Sjl 
167725cf1a30Sjl 	for (ml = mlist; ml; ml = mlist) {
167825cf1a30Sjl 		mlist = ml->next;
167925cf1a30Sjl 		kmem_free(ml, sizeof (struct memlist));
168025cf1a30Sjl 	}
168125cf1a30Sjl }
168225cf1a30Sjl 
168325cf1a30Sjl static struct memlist *
168425cf1a30Sjl mc_memlist_dup(struct memlist *mlist)
168525cf1a30Sjl {
168625cf1a30Sjl 	struct memlist *hl = NULL, *tl, **mlp;
168725cf1a30Sjl 
168825cf1a30Sjl 	if (mlist == NULL)
168925cf1a30Sjl 		return (NULL);
169025cf1a30Sjl 
169125cf1a30Sjl 	mlp = &hl;
169225cf1a30Sjl 	tl = *mlp;
169325cf1a30Sjl 	for (; mlist; mlist = mlist->next) {
169425cf1a30Sjl 		*mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP);
169525cf1a30Sjl 		(*mlp)->address = mlist->address;
169625cf1a30Sjl 		(*mlp)->size = mlist->size;
169725cf1a30Sjl 		(*mlp)->prev = tl;
169825cf1a30Sjl 		tl = *mlp;
169925cf1a30Sjl 		mlp = &((*mlp)->next);
170025cf1a30Sjl 	}
170125cf1a30Sjl 	*mlp = NULL;
170225cf1a30Sjl 
170325cf1a30Sjl 	return (hl);
170425cf1a30Sjl }
170525cf1a30Sjl 
170625cf1a30Sjl 
170725cf1a30Sjl static struct memlist *
170825cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len)
170925cf1a30Sjl {
171025cf1a30Sjl 	uint64_t	end;
171125cf1a30Sjl 	struct memlist	*ml, *tl, *nlp;
171225cf1a30Sjl 
171325cf1a30Sjl 	if (mlist == NULL)
171425cf1a30Sjl 		return (NULL);
171525cf1a30Sjl 
171625cf1a30Sjl 	end = base + len;
171725cf1a30Sjl 	if ((end <= mlist->address) || (base == end))
171825cf1a30Sjl 		return (mlist);
171925cf1a30Sjl 
172025cf1a30Sjl 	for (tl = ml = mlist; ml; tl = ml, ml = nlp) {
172125cf1a30Sjl 		uint64_t	mend;
172225cf1a30Sjl 
172325cf1a30Sjl 		nlp = ml->next;
172425cf1a30Sjl 
172525cf1a30Sjl 		if (end <= ml->address)
172625cf1a30Sjl 			break;
172725cf1a30Sjl 
172825cf1a30Sjl 		mend = ml->address + ml->size;
172925cf1a30Sjl 		if (base < mend) {
173025cf1a30Sjl 			if (base <= ml->address) {
173125cf1a30Sjl 				ml->address = end;
173225cf1a30Sjl 				if (end >= mend)
173325cf1a30Sjl 					ml->size = 0ull;
173425cf1a30Sjl 				else
173525cf1a30Sjl 					ml->size = mend - ml->address;
173625cf1a30Sjl 			} else {
173725cf1a30Sjl 				ml->size = base - ml->address;
173825cf1a30Sjl 				if (end < mend) {
173925cf1a30Sjl 					struct memlist	*nl;
174025cf1a30Sjl 					/*
174125cf1a30Sjl 					 * splitting an memlist entry.
174225cf1a30Sjl 					 */
174325cf1a30Sjl 					nl = kmem_alloc(sizeof (struct memlist),
174425cf1a30Sjl 						KM_SLEEP);
174525cf1a30Sjl 					nl->address = end;
174625cf1a30Sjl 					nl->size = mend - nl->address;
174725cf1a30Sjl 					if ((nl->next = nlp) != NULL)
174825cf1a30Sjl 						nlp->prev = nl;
174925cf1a30Sjl 					nl->prev = ml;
175025cf1a30Sjl 					ml->next = nl;
175125cf1a30Sjl 					nlp = nl;
175225cf1a30Sjl 				}
175325cf1a30Sjl 			}
175425cf1a30Sjl 			if (ml->size == 0ull) {
175525cf1a30Sjl 				if (ml == mlist) {
175625cf1a30Sjl 					if ((mlist = nlp) != NULL)
175725cf1a30Sjl 						nlp->prev = NULL;
175825cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
175925cf1a30Sjl 					if (mlist == NULL)
176025cf1a30Sjl 						break;
176125cf1a30Sjl 					ml = nlp;
176225cf1a30Sjl 				} else {
176325cf1a30Sjl 					if ((tl->next = nlp) != NULL)
176425cf1a30Sjl 						nlp->prev = tl;
176525cf1a30Sjl 					kmem_free(ml, sizeof (struct memlist));
176625cf1a30Sjl 					ml = tl;
176725cf1a30Sjl 				}
176825cf1a30Sjl 			}
176925cf1a30Sjl 		}
177025cf1a30Sjl 	}
177125cf1a30Sjl 
177225cf1a30Sjl 	return (mlist);
177325cf1a30Sjl }
177425cf1a30Sjl 
177525cf1a30Sjl static void
177625cf1a30Sjl mc_get_mlist(mc_opl_t *mcp)
177725cf1a30Sjl {
177825cf1a30Sjl 	struct memlist *mlist;
177925cf1a30Sjl 
178025cf1a30Sjl 	memlist_read_lock();
178125cf1a30Sjl 	mlist = mc_memlist_dup(phys_install);
178225cf1a30Sjl 	memlist_read_unlock();
178325cf1a30Sjl 
178425cf1a30Sjl 	if (mlist) {
178525cf1a30Sjl 		mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address);
178625cf1a30Sjl 	}
178725cf1a30Sjl 
178825cf1a30Sjl 	if (mlist) {
178925cf1a30Sjl 		uint64_t startpa, endpa;
179025cf1a30Sjl 
179125cf1a30Sjl 		startpa = mcp->mc_start_address + mcp->mc_size;
179225cf1a30Sjl 		endpa = ptob(physmax + 1);
179325cf1a30Sjl 		if (endpa > startpa) {
179425cf1a30Sjl 			mlist = mc_memlist_del_span(mlist,
179525cf1a30Sjl 				startpa, endpa - startpa);
179625cf1a30Sjl 		}
179725cf1a30Sjl 	}
179825cf1a30Sjl 
179925cf1a30Sjl 	if (mlist) {
180025cf1a30Sjl 		mcp->mlist = mlist;
180125cf1a30Sjl 	}
180225cf1a30Sjl }
180325cf1a30Sjl 
180425cf1a30Sjl int
180525cf1a30Sjl mc_board_add(mc_opl_t *mcp)
180625cf1a30Sjl {
180725cf1a30Sjl 	struct mc_addr_spec *macaddr;
180825cf1a30Sjl 	int len, i, bk, cc;
180925cf1a30Sjl 	mc_addr_info_t maddr;
181025cf1a30Sjl 	uint32_t mirr;
181125cf1a30Sjl 
181225cf1a30Sjl 	mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL);
181325cf1a30Sjl 
181425cf1a30Sjl 	/*
181525cf1a30Sjl 	 * Get configurations from "pseudo-mc" node which includes:
181625cf1a30Sjl 	 * board# : LSB number
181725cf1a30Sjl 	 * mac-addr : physical base address of MAC registers
181825cf1a30Sjl 	 * csX-mac-pa-trans-table: translation table from DIMM address
181925cf1a30Sjl 	 *			to physical address or vice versa.
182025cf1a30Sjl 	 */
182125cf1a30Sjl 	mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip,
182225cf1a30Sjl 		DDI_PROP_DONTPASS, "board#", -1);
182325cf1a30Sjl 
182425cf1a30Sjl 	/*
182525cf1a30Sjl 	 * Get start address in this CAB. It can be gotten from
182625cf1a30Sjl 	 * "sb-mem-ranges" property.
182725cf1a30Sjl 	 */
182825cf1a30Sjl 
182925cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
183025cf1a30Sjl 		mutex_destroy(&mcp->mc_lock);
183125cf1a30Sjl 		return (DDI_FAILURE);
183225cf1a30Sjl 	}
183325cf1a30Sjl 	/* get mac-pa trans tables */
183425cf1a30Sjl 	for (i = 0; i < MC_TT_CS; i++) {
183525cf1a30Sjl 		len = MC_TT_ENTRIES;
183625cf1a30Sjl 		cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip,
183725cf1a30Sjl 			DDI_PROP_DONTPASS, mc_tbl_name[i],
183825cf1a30Sjl 			(caddr_t)mcp->mc_trans_table[i], &len);
183925cf1a30Sjl 
184025cf1a30Sjl 		if (cc != DDI_SUCCESS) {
184125cf1a30Sjl 			bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES);
184225cf1a30Sjl 		}
184325cf1a30Sjl 	}
184425cf1a30Sjl 	mcp->mlist = NULL;
184525cf1a30Sjl 
184625cf1a30Sjl 	mc_get_mlist(mcp);
184725cf1a30Sjl 
184825cf1a30Sjl 	/* initialize bank informations */
184925cf1a30Sjl 	cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS,
185025cf1a30Sjl 		"mc-addr", (caddr_t)&macaddr, &len);
185125cf1a30Sjl 	if (cc != DDI_SUCCESS) {
185225cf1a30Sjl 		cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc);
185325cf1a30Sjl 		mutex_destroy(&mcp->mc_lock);
185425cf1a30Sjl 		return (DDI_FAILURE);
185525cf1a30Sjl 	}
185625cf1a30Sjl 
185725cf1a30Sjl 	for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) {
185825cf1a30Sjl 		struct mc_bank *bankp;
185925cf1a30Sjl 		uint32_t reg;
186025cf1a30Sjl 
186125cf1a30Sjl 		/*
186225cf1a30Sjl 		 * setup bank
186325cf1a30Sjl 		 */
186425cf1a30Sjl 		bk = macaddr[i].bank;
186525cf1a30Sjl 		bankp = &(mcp->mc_bank[bk]);
186625cf1a30Sjl 		bankp->mcb_status = BANK_INSTALLED;
186725cf1a30Sjl 		bankp->mcb_reg_base = REGS_PA(macaddr, i);
186825cf1a30Sjl 
186925cf1a30Sjl 		reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk));
187025cf1a30Sjl 		bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS);
187125cf1a30Sjl 
187225cf1a30Sjl 		/*
187325cf1a30Sjl 		 * check if mirror mode
187425cf1a30Sjl 		 */
187525cf1a30Sjl 		mirr = LD_MAC_REG(MAC_MIRR(mcp, bk));
187625cf1a30Sjl 
187725cf1a30Sjl 		if (mirr & MAC_MIRR_MIRROR_MODE) {
187825cf1a30Sjl 			MC_LOG("Mirror -> /LSB%d/B%d\n",
187925cf1a30Sjl 				mcp->mc_board_num, bk);
188025cf1a30Sjl 			bankp->mcb_status |= BANK_MIRROR_MODE;
188125cf1a30Sjl 			/*
188225cf1a30Sjl 			 * The following bit is only used for
188325cf1a30Sjl 			 * error injection.  We should clear it
188425cf1a30Sjl 			 */
188525cf1a30Sjl 			if (mirr & MAC_MIRR_BANK_EXCLUSIVE)
188625cf1a30Sjl 				ST_MAC_REG(MAC_MIRR(mcp, bk),
188725cf1a30Sjl 					0);
188825cf1a30Sjl 		}
188925cf1a30Sjl 
189025cf1a30Sjl 		/*
189125cf1a30Sjl 		 * restart if not mirror mode or the other bank
189225cf1a30Sjl 		 * of the mirror is not running
189325cf1a30Sjl 		 */
189425cf1a30Sjl 		if (!(mirr & MAC_MIRR_MIRROR_MODE) ||
189525cf1a30Sjl 			!(mcp->mc_bank[bk^1].mcb_status &
189625cf1a30Sjl 			BANK_PTRL_RUNNING)) {
189725cf1a30Sjl 			MC_LOG("Starting up /LSB%d/B%d\n",
189825cf1a30Sjl 				mcp->mc_board_num, bk);
189925cf1a30Sjl 			get_ptrl_start_address(mcp, bk, &maddr.mi_maddr);
190025cf1a30Sjl 			maddr.mi_valid = 1;
190125cf1a30Sjl 			maddr.mi_advance = 0;
190225cf1a30Sjl 			restart_patrol(mcp, bk, &maddr);
190325cf1a30Sjl 		} else {
190425cf1a30Sjl 			MC_LOG("Not starting up /LSB%d/B%d\n",
190525cf1a30Sjl 				mcp->mc_board_num, bk);
190625cf1a30Sjl 		}
190725cf1a30Sjl 		bankp->mcb_status |= BANK_PTRL_RUNNING;
190825cf1a30Sjl 	}
190925cf1a30Sjl 	kmem_free(macaddr, len);
191025cf1a30Sjl 
191125cf1a30Sjl 	/*
191225cf1a30Sjl 	 * set interval in HZ.
191325cf1a30Sjl 	 */
191425cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
191525cf1a30Sjl 		mcp->mc_scf_retry[i] = 0;
191625cf1a30Sjl 	}
191725cf1a30Sjl 	mcp->mc_last_error = 0;
191825cf1a30Sjl 	mcp->mc_period = 0;
191925cf1a30Sjl 
192025cf1a30Sjl 	mcp->mc_interval_hz = drv_usectohz(mc_patrol_interval_sec * 1000000);
192125cf1a30Sjl 	/* restart memory patrol checking */
192225cf1a30Sjl 	mcp->mc_status |= MC_POLL_RUNNING;
192325cf1a30Sjl 	mcp->mc_tid = timeout(mc_check_errors, mcp, mcp->mc_interval_hz);
192425cf1a30Sjl 
192525cf1a30Sjl 	return (DDI_SUCCESS);
192625cf1a30Sjl }
192725cf1a30Sjl 
192825cf1a30Sjl int
192925cf1a30Sjl mc_board_del(mc_opl_t *mcp)
193025cf1a30Sjl {
193125cf1a30Sjl 	int i;
193225cf1a30Sjl 	scf_log_t *p;
193325cf1a30Sjl 	timeout_id_t tid = 0;
193425cf1a30Sjl 
193525cf1a30Sjl 	/*
193625cf1a30Sjl 	 * cleanup mac state
193725cf1a30Sjl 	 */
193825cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
193925cf1a30Sjl 	for (i = 0; i < BANKNUM_PER_SB; i++) {
194025cf1a30Sjl 		if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
194125cf1a30Sjl 			if (mc_stop(mcp, i)) {
194225cf1a30Sjl 				mutex_exit(&mcp->mc_lock);
194325cf1a30Sjl 				return (-1);
194425cf1a30Sjl 			}
194525cf1a30Sjl 			mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED;
194625cf1a30Sjl 		}
194725cf1a30Sjl 	}
194825cf1a30Sjl 
194925cf1a30Sjl 	/* stop memory patrol checking */
195025cf1a30Sjl 	if (mcp->mc_status & MC_POLL_RUNNING) {
195125cf1a30Sjl 		mcp->mc_status &= ~MC_POLL_RUNNING;
195225cf1a30Sjl 		tid = mcp->mc_tid;
195325cf1a30Sjl 		mcp->mc_tid = 0;
195425cf1a30Sjl 	}
195525cf1a30Sjl 
195625cf1a30Sjl 	/* just throw away all the scf logs */
195725cf1a30Sjl 	while ((p = mcp->mc_scf_log) != NULL) {
195825cf1a30Sjl 		mcp->mc_scf_log = p->sl_next;
195925cf1a30Sjl 		mcp->mc_scf_total--;
196025cf1a30Sjl 		kmem_free(p, sizeof (scf_log_t));
196125cf1a30Sjl 	}
196225cf1a30Sjl 
196325cf1a30Sjl 	if (mcp->mlist)
196425cf1a30Sjl 		mc_memlist_delete(mcp->mlist);
196525cf1a30Sjl 
196625cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
196725cf1a30Sjl 	if (tid)
196825cf1a30Sjl 		(void) untimeout(tid);
196925cf1a30Sjl 
197025cf1a30Sjl 	mutex_destroy(&mcp->mc_lock);
197125cf1a30Sjl 	return (DDI_SUCCESS);
197225cf1a30Sjl }
197325cf1a30Sjl 
197425cf1a30Sjl int
197525cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag)
197625cf1a30Sjl {
197725cf1a30Sjl 	timeout_id_t tid = 0;
197825cf1a30Sjl 	int i;
197925cf1a30Sjl 	/* stop memory patrol checking */
198025cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
198125cf1a30Sjl 	if (mcp->mc_status & MC_POLL_RUNNING) {
198225cf1a30Sjl 		for (i = 0; i < BANKNUM_PER_SB; i++) {
198325cf1a30Sjl 			if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
198425cf1a30Sjl 				if (mc_stop(mcp, i)) {
198525cf1a30Sjl 					mutex_exit(&mcp->mc_lock);
198625cf1a30Sjl 					return (-1);
198725cf1a30Sjl 				}
198825cf1a30Sjl 			}
198925cf1a30Sjl 		}
199025cf1a30Sjl 		mcp->mc_status &= ~MC_POLL_RUNNING;
199125cf1a30Sjl 		tid = mcp->mc_tid;
199225cf1a30Sjl 	}
199325cf1a30Sjl 	mcp->mc_status |= flag;
199425cf1a30Sjl 	mcp->mc_tid = 0;
199525cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
199625cf1a30Sjl 	if (tid)
199725cf1a30Sjl 		(void) untimeout(tid);
199825cf1a30Sjl 
199925cf1a30Sjl 	return (DDI_SUCCESS);
200025cf1a30Sjl }
200125cf1a30Sjl 
200225cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */
200325cf1a30Sjl 
200425cf1a30Sjl int
200525cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag)
200625cf1a30Sjl {
200725cf1a30Sjl 	int i;
200825cf1a30Sjl 	uint64_t basepa;
200925cf1a30Sjl 
201025cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
201125cf1a30Sjl 	basepa = mcp->mc_start_address;
201225cf1a30Sjl 	if (get_base_address(mcp) == DDI_FAILURE) {
201325cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
201425cf1a30Sjl 		return (DDI_FAILURE);
201525cf1a30Sjl 	}
201625cf1a30Sjl 
201725cf1a30Sjl 	if (basepa != mcp->mc_start_address) {
201825cf1a30Sjl 		if (mcp->mlist)
201925cf1a30Sjl 			mc_memlist_delete(mcp->mlist);
202025cf1a30Sjl 		mcp->mlist = NULL;
202125cf1a30Sjl 		mc_get_mlist(mcp);
202225cf1a30Sjl 	}
202325cf1a30Sjl 
202425cf1a30Sjl 	mcp->mc_status &= ~flag;
202525cf1a30Sjl 	mcp->mc_list->mc_start_address = mcp->mc_start_address;
202625cf1a30Sjl 
202725cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
202825cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
202925cf1a30Sjl 		return (DDI_SUCCESS);
203025cf1a30Sjl 	}
203125cf1a30Sjl 
203225cf1a30Sjl 	if (!(mcp->mc_status & MC_POLL_RUNNING)) {
203325cf1a30Sjl 		/* restart memory patrol checking */
203425cf1a30Sjl 		mcp->mc_status |= MC_POLL_RUNNING;
203525cf1a30Sjl 		for (i = 0; i < BANKNUM_PER_SB; i++) {
203625cf1a30Sjl 			if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) {
203725cf1a30Sjl 				restart_patrol(mcp, i, NULL);
203825cf1a30Sjl 			}
203925cf1a30Sjl 		}
204025cf1a30Sjl 		/* check error asap */
204125cf1a30Sjl 		mcp->mc_tid = timeout(mc_check_errors, mcp, 1);
204225cf1a30Sjl 	}
204325cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
204425cf1a30Sjl 
204525cf1a30Sjl 	return (DDI_SUCCESS);
204625cf1a30Sjl }
204725cf1a30Sjl 
204825cf1a30Sjl static mc_opl_t *
204925cf1a30Sjl mc_pa_to_mcp(uint64_t pa)
205025cf1a30Sjl {
205125cf1a30Sjl 	mc_inst_list_t *p;
205225cf1a30Sjl 	ASSERT(MUTEX_HELD(&mcmutex));
205325cf1a30Sjl 	for (p = mc_instances; p; p = p->next) {
205425cf1a30Sjl 		/* if mac patrol is suspended, we cannot rely on it */
205525cf1a30Sjl 		if (!(p->mc_opl->mc_status & MC_POLL_RUNNING) ||
205625cf1a30Sjl 			(p->mc_opl->mc_status & MC_SOFT_SUSPENDED))
205725cf1a30Sjl 			continue;
205825cf1a30Sjl 		if ((p->mc_start_address <= pa) &&
205925cf1a30Sjl 			(pa < (p->mc_start_address + p->mc_size))) {
206025cf1a30Sjl 			return (p->mc_opl);
206125cf1a30Sjl 		}
206225cf1a30Sjl 	}
206325cf1a30Sjl 	return (NULL);
206425cf1a30Sjl }
206525cf1a30Sjl 
206625cf1a30Sjl /*
206725cf1a30Sjl  * Get Physical Board number from Logical one.
206825cf1a30Sjl  */
206925cf1a30Sjl static int
207025cf1a30Sjl mc_opl_get_physical_board(int sb)
207125cf1a30Sjl {
207225cf1a30Sjl 	if (&opl_get_physical_board) {
207325cf1a30Sjl 		return (opl_get_physical_board(sb));
207425cf1a30Sjl 	}
207525cf1a30Sjl 
207625cf1a30Sjl 	cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n");
207725cf1a30Sjl 	return (-1);
207825cf1a30Sjl }
207925cf1a30Sjl 
208025cf1a30Sjl /* ARGSUSED */
208125cf1a30Sjl int
208225cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen,
208325cf1a30Sjl 	int *lenp)
208425cf1a30Sjl {
208525cf1a30Sjl 	mc_opl_t *mcp;
208625cf1a30Sjl 	int bank;
208725cf1a30Sjl 	int sb;
208825cf1a30Sjl 
208925cf1a30Sjl 	mutex_enter(&mcmutex);
209025cf1a30Sjl 
209125cf1a30Sjl 	if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) ||
209225cf1a30Sjl 		(!pa_is_valid(mcp, flt_addr))) {
209325cf1a30Sjl 		mutex_exit(&mcmutex);
209425cf1a30Sjl 		if (snprintf(buf, buflen, "UNKNOWN") >= buflen) {
209525cf1a30Sjl 			return (ENOSPC);
209625cf1a30Sjl 		} else {
209725cf1a30Sjl 			if (lenp)
209825cf1a30Sjl 				*lenp = strlen(buf);
209925cf1a30Sjl 		}
210025cf1a30Sjl 		return (0);
210125cf1a30Sjl 	}
210225cf1a30Sjl 
210325cf1a30Sjl 	bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address);
210425cf1a30Sjl 	sb = mc_opl_get_physical_board(mcp->mc_board_num);
210525cf1a30Sjl 
210625cf1a30Sjl 	if (sb == -1) {
210725cf1a30Sjl 		mutex_exit(&mcmutex);
210825cf1a30Sjl 		return (ENXIO);
210925cf1a30Sjl 	}
211025cf1a30Sjl 
211125cf1a30Sjl 	if (snprintf(buf, buflen, "/CMU%d/B%d", sb, bank) >= buflen) {
211225cf1a30Sjl 		mutex_exit(&mcmutex);
211325cf1a30Sjl 		return (ENOSPC);
211425cf1a30Sjl 	} else {
211525cf1a30Sjl 		if (lenp)
211625cf1a30Sjl 			*lenp = strlen(buf);
211725cf1a30Sjl 	}
211825cf1a30Sjl 	mutex_exit(&mcmutex);
211925cf1a30Sjl 	return (0);
212025cf1a30Sjl }
212125cf1a30Sjl 
212225cf1a30Sjl int
212325cf1a30Sjl opl_mc_suspend()
212425cf1a30Sjl {
212525cf1a30Sjl 	mc_opl_t *mcp;
212625cf1a30Sjl 	mc_inst_list_t *p;
212725cf1a30Sjl 
212825cf1a30Sjl 	mutex_enter(&mcmutex);
212925cf1a30Sjl 
213025cf1a30Sjl 	for (p = mc_instances; p; p = p->next) {
213125cf1a30Sjl 		mcp = p->mc_opl;
213225cf1a30Sjl 		(void) mc_suspend(mcp, MC_SOFT_SUSPENDED);
213325cf1a30Sjl 	}
213425cf1a30Sjl 
213525cf1a30Sjl 	mutex_exit(&mcmutex);
213625cf1a30Sjl 	return (0);
213725cf1a30Sjl }
213825cf1a30Sjl 
213925cf1a30Sjl int
214025cf1a30Sjl opl_mc_resume()
214125cf1a30Sjl {
214225cf1a30Sjl 	mc_opl_t *mcp;
214325cf1a30Sjl 	mc_inst_list_t *p;
214425cf1a30Sjl 
214525cf1a30Sjl 	mutex_enter(&mcmutex);
214625cf1a30Sjl 
214725cf1a30Sjl 	for (p = mc_instances; p; p = p->next) {
214825cf1a30Sjl 		mcp = p->mc_opl;
214925cf1a30Sjl 		(void) mc_resume(mcp, MC_SOFT_SUSPENDED);
215025cf1a30Sjl 	}
215125cf1a30Sjl 
215225cf1a30Sjl 	mutex_exit(&mcmutex);
215325cf1a30Sjl 	return (0);
215425cf1a30Sjl }
215525cf1a30Sjl 
215625cf1a30Sjl static void
215725cf1a30Sjl insert_mcp(mc_opl_t *mcp)
215825cf1a30Sjl {
215925cf1a30Sjl 	mc_inst_list_t	*p;
216025cf1a30Sjl 
216125cf1a30Sjl 	p = kmem_zalloc(sizeof (mc_inst_list_t), KM_SLEEP);
216225cf1a30Sjl 	p->mc_opl = mcp;
216325cf1a30Sjl 	p->mc_board_num = mcp->mc_board_num;
216425cf1a30Sjl 	p->mc_start_address = mcp->mc_start_address;
216525cf1a30Sjl 	p->mc_size = mcp->mc_size;
216625cf1a30Sjl 	mcp->mc_list = p;
216725cf1a30Sjl 
216825cf1a30Sjl 	mutex_enter(&mcmutex);
216925cf1a30Sjl 
217025cf1a30Sjl 	p->next = mc_instances;
217125cf1a30Sjl 	mc_instances = p;
217225cf1a30Sjl 
217325cf1a30Sjl 	mutex_exit(&mcmutex);
217425cf1a30Sjl }
217525cf1a30Sjl 
217625cf1a30Sjl static void
217725cf1a30Sjl delete_mcp(mc_opl_t *mcp)
217825cf1a30Sjl {
217925cf1a30Sjl 	mc_inst_list_t *prev, *current;
218025cf1a30Sjl 	mc_inst_list_t *p;
218125cf1a30Sjl 
218225cf1a30Sjl 	p = mcp->mc_list;
218325cf1a30Sjl 
218425cf1a30Sjl 	if (mc_instances == p) {
218525cf1a30Sjl 		mc_instances = p->next;
218625cf1a30Sjl 		kmem_free(p, sizeof (mc_inst_list_t));
218725cf1a30Sjl 		return;
218825cf1a30Sjl 	}
218925cf1a30Sjl 	prev = mc_instances;
219025cf1a30Sjl 	for (current = mc_instances; current != NULL; current = current->next) {
219125cf1a30Sjl 		if (current == p) {
219225cf1a30Sjl 			prev->next = p->next;
219325cf1a30Sjl 			kmem_free(p, sizeof (mc_inst_list_t));
219425cf1a30Sjl 			return;
219525cf1a30Sjl 		}
219625cf1a30Sjl 		prev = current;
219725cf1a30Sjl 	}
219825cf1a30Sjl }
219925cf1a30Sjl 
220025cf1a30Sjl /* Error injection interface */
220125cf1a30Sjl 
220225cf1a30Sjl /* ARGSUSED */
220325cf1a30Sjl int
220425cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags)
220525cf1a30Sjl {
220625cf1a30Sjl 	mc_opl_t *mcp;
220725cf1a30Sjl 	int bank;
220825cf1a30Sjl 	uint32_t dimm_addr;
220925cf1a30Sjl 	uint32_t cntl;
221025cf1a30Sjl 	mc_addr_info_t maddr;
221125cf1a30Sjl 	uint32_t data, stat;
221225cf1a30Sjl 	int both_sides = 0;
221325cf1a30Sjl 	uint64_t pa0;
221425cf1a30Sjl 	on_trap_data_t otd;
221525cf1a30Sjl 	extern void cpu_flush_ecache(void);
221625cf1a30Sjl 
221725cf1a30Sjl 	MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags);
221825cf1a30Sjl 
221925cf1a30Sjl 	mutex_enter(&mcmutex);
222025cf1a30Sjl 
222125cf1a30Sjl 	if ((mcp = mc_pa_to_mcp(pa)) == NULL) {
222225cf1a30Sjl 		mutex_exit(&mcmutex);
222325cf1a30Sjl 		MC_LOG("mc_inject_error: invalid pa\n");
222425cf1a30Sjl 		return (ENOTSUP);
222525cf1a30Sjl 	}
222625cf1a30Sjl 
222725cf1a30Sjl 	mutex_enter(&mcp->mc_lock);
222825cf1a30Sjl 	mutex_exit(&mcmutex);
222925cf1a30Sjl 
223025cf1a30Sjl 	if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) {
223125cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
223225cf1a30Sjl 		MC_LOG("mc-opl has been suspended.  No error injection.\n");
223325cf1a30Sjl 		return (EBUSY);
223425cf1a30Sjl 	}
223525cf1a30Sjl 
223625cf1a30Sjl 	/* convert pa to offset within the board */
223725cf1a30Sjl 	MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address);
223825cf1a30Sjl 
223925cf1a30Sjl 	if (!pa_is_valid(mcp, pa)) {
224025cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
224125cf1a30Sjl 		return (EINVAL);
224225cf1a30Sjl 	}
224325cf1a30Sjl 
224425cf1a30Sjl 	pa0 = pa - mcp->mc_start_address;
224525cf1a30Sjl 
224625cf1a30Sjl 	bank = pa_to_bank(mcp, pa0);
224725cf1a30Sjl 
224825cf1a30Sjl 	if (flags & MC_INJECT_FLAG_OTHER)
224925cf1a30Sjl 		bank = bank ^ 1;
225025cf1a30Sjl 
225125cf1a30Sjl 	if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) {
225225cf1a30Sjl 		mutex_exit(&mcp->mc_lock);
225325cf1a30Sjl 		MC_LOG("Not mirror mode\n");
225425cf1a30Sjl 		return (EINVAL);
225525cf1a30Sjl 	}
225625cf1a30Sjl 
225725cf1a30Sjl 	dimm_addr = pa_to_dimm(mcp, pa0);
225825cf1a30Sjl 
225925cf1a30Sjl 	MC_LOG("injecting error to /LSB%d/B%d/D%x\n",
226025cf1a30Sjl 		mcp->mc_board_num, bank, dimm_addr);
226125cf1a30Sjl 
226225cf1a30Sjl 
226325cf1a30Sjl 	switch (error_type) {
226425cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
226525cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
226625cf1a30Sjl 	case MC_INJECT_MUE:
226725cf1a30Sjl 		both_sides = 1;
226825cf1a30Sjl 	}
226925cf1a30Sjl 
227025cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESET)
227125cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0);
227225cf1a30Sjl 
227325cf1a30Sjl 	ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK);
227425cf1a30Sjl 
227525cf1a30Sjl 	if (both_sides) {
227625cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0);
227725cf1a30Sjl 		ST_MAC_REG(MAC_EG_ADD(mcp, bank^1),
227825cf1a30Sjl 			dimm_addr & MAC_EG_ADD_MASK);
227925cf1a30Sjl 	}
228025cf1a30Sjl 
228125cf1a30Sjl 	switch (error_type) {
228225cf1a30Sjl 	case MC_INJECT_UE:
228325cf1a30Sjl 	case MC_INJECT_SUE:
228425cf1a30Sjl 	case MC_INJECT_MUE:
228525cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
228625cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
228725cf1a30Sjl 				|MAC_EG_FORCE_READ00|MAC_EG_FORCE_READ16
228825cf1a30Sjl 				|MAC_EG_DERR_ONCE;
228925cf1a30Sjl 		} else {
229025cf1a30Sjl 			cntl = MAC_EG_ADD_FIX|MAC_EG_FORCE_DERR00
229125cf1a30Sjl 				|MAC_EG_FORCE_DERR16|MAC_EG_DERR_ONCE;
229225cf1a30Sjl 		}
229325cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
229425cf1a30Sjl 		break;
229525cf1a30Sjl 	case MC_INJECT_INTERMITTENT_CE:
229625cf1a30Sjl 	case MC_INJECT_INTERMITTENT_MCE:
229725cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
229825cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
229925cf1a30Sjl 				|MAC_EG_FORCE_READ00
230025cf1a30Sjl 				|MAC_EG_DERR_ONCE;
230125cf1a30Sjl 		} else {
230225cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
230325cf1a30Sjl 				|MAC_EG_FORCE_DERR16
230425cf1a30Sjl 				|MAC_EG_DERR_ONCE;
230525cf1a30Sjl 		}
230625cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
230725cf1a30Sjl 		break;
230825cf1a30Sjl 	case MC_INJECT_PERMANENT_CE:
230925cf1a30Sjl 	case MC_INJECT_PERMANENT_MCE:
231025cf1a30Sjl 		if (flags & MC_INJECT_FLAG_PATH) {
231125cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
231225cf1a30Sjl 				|MAC_EG_FORCE_READ00
231325cf1a30Sjl 				|MAC_EG_DERR_ALWAYS;
231425cf1a30Sjl 		} else {
231525cf1a30Sjl 			cntl = MAC_EG_ADD_FIX
231625cf1a30Sjl 				|MAC_EG_FORCE_DERR16
231725cf1a30Sjl 				|MAC_EG_DERR_ALWAYS;
231825cf1a30Sjl 		}
231925cf1a30Sjl 		flags |= MC_INJECT_FLAG_ST;
232025cf1a30Sjl 		break;
232125cf1a30Sjl 	case MC_INJECT_CMPE:
232225cf1a30Sjl 		data = 0xabcdefab;
232325cf1a30Sjl 		stphys(pa, data);
232425cf1a30Sjl 		cpu_flush_ecache();
232525cf1a30Sjl 		MC_LOG("CMPE: writing data %x to %lx\n", data, pa);
232625cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE);
232725cf1a30Sjl 		stphys(pa, data ^ 0xffffffff);
232825cf1a30Sjl 		cpu_flush_ecache();
232925cf1a30Sjl 		ST_MAC_REG(MAC_MIRR(mcp, bank), 0);
233025cf1a30Sjl 		MC_LOG("CMPE: write new data %xto %lx\n", data, pa);
233125cf1a30Sjl 		cntl = 0;
233225cf1a30Sjl 		break;
233325cf1a30Sjl 	case MC_INJECT_NOP:
233425cf1a30Sjl 		cntl = 0;
233525cf1a30Sjl 		break;
233625cf1a30Sjl 	default:
233725cf1a30Sjl 		MC_LOG("mc_inject_error: invalid option\n");
233825cf1a30Sjl 		cntl = 0;
233925cf1a30Sjl 	}
234025cf1a30Sjl 
234125cf1a30Sjl 	if (cntl) {
234225cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK);
234325cf1a30Sjl 		ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl);
234425cf1a30Sjl 
234525cf1a30Sjl 		if (both_sides) {
234625cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl &
234725cf1a30Sjl 				MAC_EG_SETUP_MASK);
234825cf1a30Sjl 			ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl);
234925cf1a30Sjl 		}
235025cf1a30Sjl 	}
235125cf1a30Sjl 
235225cf1a30Sjl 	/*
235325cf1a30Sjl 	 * For all injection cases except compare error, we
235425cf1a30Sjl 	 * must write to the PA to trigger the error.
235525cf1a30Sjl 	 */
235625cf1a30Sjl 
235725cf1a30Sjl 	if (flags & MC_INJECT_FLAG_ST) {
235825cf1a30Sjl 		data = 0xf0e0d0c0;
235925cf1a30Sjl 		MC_LOG("Writing %x to %lx\n", data, pa);
236025cf1a30Sjl 		stphys(pa, data);
236125cf1a30Sjl 		cpu_flush_ecache();
236225cf1a30Sjl 	}
236325cf1a30Sjl 
236425cf1a30Sjl 	delay(inject_op_delay * drv_usectohz(1000 * 1000));
236525cf1a30Sjl 
236625cf1a30Sjl 
236725cf1a30Sjl 	if (flags & MC_INJECT_FLAG_LD) {
236825cf1a30Sjl 		if (flags & MC_INJECT_FLAG_NO_TRAP) {
236925cf1a30Sjl 			if (on_trap(&otd, OT_DATA_EC)) {
237025cf1a30Sjl 				no_trap();
237125cf1a30Sjl 				MC_LOG("Trap occurred\n");
237225cf1a30Sjl 			} else {
237325cf1a30Sjl 				MC_LOG("On-trap Reading from %lx\n", pa);
237425cf1a30Sjl 				data = ldphys(pa);
237525cf1a30Sjl 				no_trap();
237625cf1a30Sjl 				MC_LOG("data = %x\n", data);
237725cf1a30Sjl 			}
237825cf1a30Sjl 		} else {
237925cf1a30Sjl 			MC_LOG("Reading from %lx\n", pa);
238025cf1a30Sjl 			data = ldphys(pa);
238125cf1a30Sjl 			MC_LOG("data = %x\n", data);
238225cf1a30Sjl 		}
238325cf1a30Sjl 	}
238425cf1a30Sjl 
238525cf1a30Sjl 	if (flags & MC_INJECT_FLAG_RESTART) {
238625cf1a30Sjl 		delay(inject_op_delay * drv_usectohz(1000 * 1000));
238725cf1a30Sjl 
238825cf1a30Sjl 		MC_LOG("Restart patrol\n");
238925cf1a30Sjl 		if (mc_stop(mcp, bank)) {
239025cf1a30Sjl 			cmn_err(CE_WARN, "Cannot stop Memory Patrol at "
239125cf1a30Sjl 				"/LSB%d/B%d\n", mcp->mc_board_num, bank);
239225cf1a30Sjl 			mutex_exit(&mcp->mc_lock);
239325cf1a30Sjl 			return (EIO);
239425cf1a30Sjl 		}
239525cf1a30Sjl 		maddr.mi_maddr.ma_bd = mcp->mc_board_num;
239625cf1a30Sjl 		maddr.mi_maddr.ma_bank = bank;
239725cf1a30Sjl 		maddr.mi_maddr.ma_dimm_addr = dimm_addr;
239825cf1a30Sjl 		maddr.mi_valid = 1;
239925cf1a30Sjl 		maddr.mi_advance = 0;
240025cf1a30Sjl 		restart_patrol(mcp, bank, &maddr);
240125cf1a30Sjl 	}
240225cf1a30Sjl 
240325cf1a30Sjl 	if (flags & MC_INJECT_FLAG_POLL) {
240425cf1a30Sjl 		delay(inject_op_delay * drv_usectohz(1000 * 1000));
240525cf1a30Sjl 
240625cf1a30Sjl 		MC_LOG("Poll patrol error\n");
240725cf1a30Sjl 		stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank));
240825cf1a30Sjl 		cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank));
240925cf1a30Sjl 		if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) {
241025cf1a30Sjl 			maddr.mi_valid = 0;
241125cf1a30Sjl 			maddr.mi_advance = 1;
241225cf1a30Sjl 			if (IS_MIRROR(mcp, bank))
241325cf1a30Sjl 				mc_error_handler_mir(mcp, bank,
241425cf1a30Sjl 					&maddr);
241525cf1a30Sjl 			else
241625cf1a30Sjl 				mc_error_handler(mcp, bank, &maddr);
241725cf1a30Sjl 
241825cf1a30Sjl 			restart_patrol(mcp, bank, &maddr);
241925cf1a30Sjl 		} else
242025cf1a30Sjl 			restart_patrol(mcp, bank, NULL);
242125cf1a30Sjl 	}
242225cf1a30Sjl 
242325cf1a30Sjl 	mutex_exit(&mcp->mc_lock);
242425cf1a30Sjl 	return (0);
242525cf1a30Sjl }
242625cf1a30Sjl 
242725cf1a30Sjl void
242825cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data)
242925cf1a30Sjl {
243025cf1a30Sjl 	MC_LOG("0x%x -> pa(%lx)\n", data, pa);
243125cf1a30Sjl 	stphysio(pa, data);
243225cf1a30Sjl }
243325cf1a30Sjl 
243425cf1a30Sjl uint32_t
243525cf1a30Sjl mc_ldphysio(uint64_t pa)
243625cf1a30Sjl {
243725cf1a30Sjl 	uint32_t rv;
243825cf1a30Sjl 
243925cf1a30Sjl 	rv = ldphysio(pa);
244025cf1a30Sjl 	MC_LOG("pa(%lx) = 0x%x\n", pa, rv);
244125cf1a30Sjl 	return (rv);
244225cf1a30Sjl }
2443