xref: /onnv-gate/usr/src/uts/sun4u/io/mc-us3.c (revision 7656:2621e50fdf4a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51463Sayznaga  * Common Development and Distribution License (the "License").
61463Sayznaga  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
226803Spothier  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <sys/types.h>
270Sstevel@tonic-gate #include <sys/conf.h>
280Sstevel@tonic-gate #include <sys/ddi.h>
290Sstevel@tonic-gate #include <sys/stat.h>
300Sstevel@tonic-gate #include <sys/sunddi.h>
310Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
320Sstevel@tonic-gate #include <sys/obpdefs.h>
330Sstevel@tonic-gate #include <sys/cmn_err.h>
340Sstevel@tonic-gate #include <sys/errno.h>
350Sstevel@tonic-gate #include <sys/kmem.h>
360Sstevel@tonic-gate #include <sys/open.h>
370Sstevel@tonic-gate #include <sys/thread.h>
380Sstevel@tonic-gate #include <sys/cpuvar.h>
390Sstevel@tonic-gate #include <sys/x_call.h>
400Sstevel@tonic-gate #include <sys/debug.h>
410Sstevel@tonic-gate #include <sys/sysmacros.h>
420Sstevel@tonic-gate #include <sys/ivintr.h>
430Sstevel@tonic-gate #include <sys/intr.h>
440Sstevel@tonic-gate #include <sys/intreg.h>
450Sstevel@tonic-gate #include <sys/autoconf.h>
460Sstevel@tonic-gate #include <sys/modctl.h>
470Sstevel@tonic-gate #include <sys/spl.h>
480Sstevel@tonic-gate #include <sys/async.h>
490Sstevel@tonic-gate #include <sys/mc.h>
500Sstevel@tonic-gate #include <sys/mc-us3.h>
510Sstevel@tonic-gate #include <sys/cpu_module.h>
521186Sayznaga #include <sys/platform_module.h>
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /*
550Sstevel@tonic-gate  * Function prototypes
560Sstevel@tonic-gate  */
570Sstevel@tonic-gate 
580Sstevel@tonic-gate static int mc_open(dev_t *, int, int, cred_t *);
590Sstevel@tonic-gate static int mc_close(dev_t, int, int, cred_t *);
600Sstevel@tonic-gate static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
610Sstevel@tonic-gate static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
620Sstevel@tonic-gate static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
630Sstevel@tonic-gate 
640Sstevel@tonic-gate /*
650Sstevel@tonic-gate  * Configuration data structures
660Sstevel@tonic-gate  */
670Sstevel@tonic-gate static struct cb_ops mc_cb_ops = {
680Sstevel@tonic-gate 	mc_open,			/* open */
690Sstevel@tonic-gate 	mc_close,			/* close */
700Sstevel@tonic-gate 	nulldev,			/* strategy */
710Sstevel@tonic-gate 	nulldev,			/* print */
720Sstevel@tonic-gate 	nodev,				/* dump */
730Sstevel@tonic-gate 	nulldev,			/* read */
740Sstevel@tonic-gate 	nulldev,			/* write */
750Sstevel@tonic-gate 	mc_ioctl,			/* ioctl */
760Sstevel@tonic-gate 	nodev,				/* devmap */
770Sstevel@tonic-gate 	nodev,				/* mmap */
780Sstevel@tonic-gate 	nodev,				/* segmap */
790Sstevel@tonic-gate 	nochpoll,			/* poll */
800Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
810Sstevel@tonic-gate 	0,				/* streamtab */
820Sstevel@tonic-gate 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
830Sstevel@tonic-gate 	CB_REV,				/* rev */
840Sstevel@tonic-gate 	nodev,				/* cb_aread */
850Sstevel@tonic-gate 	nodev				/* cb_awrite */
860Sstevel@tonic-gate };
870Sstevel@tonic-gate 
880Sstevel@tonic-gate static struct dev_ops mc_ops = {
890Sstevel@tonic-gate 	DEVO_REV,			/* rev */
900Sstevel@tonic-gate 	0,				/* refcnt  */
910Sstevel@tonic-gate 	ddi_getinfo_1to1,		/* getinfo */
920Sstevel@tonic-gate 	nulldev,			/* identify */
930Sstevel@tonic-gate 	nulldev,			/* probe */
940Sstevel@tonic-gate 	mc_attach,			/* attach */
950Sstevel@tonic-gate 	mc_detach,			/* detach */
960Sstevel@tonic-gate 	nulldev,			/* reset */
970Sstevel@tonic-gate 	&mc_cb_ops,			/* cb_ops */
980Sstevel@tonic-gate 	(struct bus_ops *)0,		/* bus_ops */
99*7656SSherry.Moore@Sun.COM 	nulldev,			/* power */
100*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,			/* quiesce */
1010Sstevel@tonic-gate };
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate /*
1040Sstevel@tonic-gate  * Driver globals
1050Sstevel@tonic-gate  */
1060Sstevel@tonic-gate static void *mcp;
1070Sstevel@tonic-gate static int nmcs = 0;
1080Sstevel@tonic-gate static int seg_id = 0;
1090Sstevel@tonic-gate static int nsegments = 0;
1100Sstevel@tonic-gate static uint64_t memsize = 0;
1110Sstevel@tonic-gate static int maxbanks = 0;
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate static mc_dlist_t *seg_head, *seg_tail, *bank_head, *bank_tail;
1140Sstevel@tonic-gate static mc_dlist_t *mctrl_head, *mctrl_tail, *dgrp_head, *dgrp_tail;
1150Sstevel@tonic-gate static mc_dlist_t *device_head, *device_tail;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate static kmutex_t	mcmutex;
1180Sstevel@tonic-gate static kmutex_t	mcdatamutex;
1190Sstevel@tonic-gate 
1201186Sayznaga static krwlock_t mcdimmsids_rw;
1211186Sayznaga 
1221186Sayznaga /* pointer to cache of DIMM serial ids */
1231186Sayznaga static dimm_sid_cache_t	*mc_dimm_sids;
1241186Sayznaga static int		max_entries;
1251186Sayznaga 
1260Sstevel@tonic-gate extern struct mod_ops mod_driverops;
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate static struct modldrv modldrv = {
1290Sstevel@tonic-gate 	&mod_driverops,			/* module type, this one is a driver */
1307632SNick.Todd@Sun.COM 	"Memory-controller",		/* module name */
1310Sstevel@tonic-gate 	&mc_ops,			/* driver ops */
1320Sstevel@tonic-gate };
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate static struct modlinkage modlinkage = {
1350Sstevel@tonic-gate 	MODREV_1,		/* rev */
1360Sstevel@tonic-gate 	(void *)&modldrv,
1370Sstevel@tonic-gate 	NULL
1380Sstevel@tonic-gate };
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate static int mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf,
1410Sstevel@tonic-gate     int buflen, int *lenp);
1420Sstevel@tonic-gate static int mc_get_mem_info(int synd_code, uint64_t paddr,
1430Sstevel@tonic-gate     uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
1440Sstevel@tonic-gate     int *segsp, int *banksp, int *mcidp);
1451186Sayznaga static int mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp);
1461186Sayznaga static int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
1471186Sayznaga static int mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr);
1481186Sayznaga static int mc_init_sid_cache(void);
1490Sstevel@tonic-gate static int mc_get_mcregs(struct mc_soft_state *);
1500Sstevel@tonic-gate static void mc_construct(int mc_id, void *dimminfop);
1510Sstevel@tonic-gate static int mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop);
1521186Sayznaga static void mlayout_del(int mc_id, int delete);
1530Sstevel@tonic-gate static struct seg_info *seg_match_base(u_longlong_t base);
1540Sstevel@tonic-gate static void mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
1550Sstevel@tonic-gate static void mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
1560Sstevel@tonic-gate static mc_dlist_t *mc_node_get(int id, mc_dlist_t *head);
1570Sstevel@tonic-gate static void mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm);
1581186Sayznaga static int mc_populate_sid_cache(void);
1591186Sayznaga static int mc_get_sid_cache_index(int mcid);
1601463Sayznaga static void mc_update_bank(struct bank_info *bank);
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate #pragma weak p2get_mem_unum
1630Sstevel@tonic-gate #pragma weak p2get_mem_info
1641186Sayznaga #pragma weak p2get_mem_sid
1651186Sayznaga #pragma weak p2get_mem_offset
1661186Sayznaga #pragma	weak p2get_mem_addr
1671186Sayznaga #pragma weak p2init_sid_cache
1680Sstevel@tonic-gate #pragma weak plat_add_mem_unum_label
1691186Sayznaga #pragma weak plat_alloc_sid_cache
1701186Sayznaga #pragma weak plat_populate_sid_cache
1711186Sayznaga 
1721186Sayznaga #define	QWORD_SIZE		144
1731186Sayznaga #define	QWORD_SIZE_BYTES	(QWORD_SIZE / 8)
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate /*
1760Sstevel@tonic-gate  * These are the module initialization routines.
1770Sstevel@tonic-gate  */
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate int
_init(void)1800Sstevel@tonic-gate _init(void)
1810Sstevel@tonic-gate {
1820Sstevel@tonic-gate 	int error;
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate 	if ((error = ddi_soft_state_init(&mcp,
1850Sstevel@tonic-gate 	    sizeof (struct mc_soft_state), 1)) != 0)
1860Sstevel@tonic-gate 		return (error);
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 	error =  mod_install(&modlinkage);
1890Sstevel@tonic-gate 	if (error == 0) {
1900Sstevel@tonic-gate 		mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
1910Sstevel@tonic-gate 		mutex_init(&mcdatamutex, NULL, MUTEX_DRIVER, NULL);
1921186Sayznaga 		rw_init(&mcdimmsids_rw, NULL, RW_DRIVER, NULL);
1930Sstevel@tonic-gate 	}
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	return (error);
1960Sstevel@tonic-gate }
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate int
_fini(void)1990Sstevel@tonic-gate _fini(void)
2000Sstevel@tonic-gate {
2010Sstevel@tonic-gate 	int error;
2020Sstevel@tonic-gate 
2030Sstevel@tonic-gate 	if ((error = mod_remove(&modlinkage)) != 0)
2040Sstevel@tonic-gate 		return (error);
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	ddi_soft_state_fini(&mcp);
2070Sstevel@tonic-gate 	mutex_destroy(&mcmutex);
2080Sstevel@tonic-gate 	mutex_destroy(&mcdatamutex);
2091186Sayznaga 	rw_destroy(&mcdimmsids_rw);
2101186Sayznaga 
2111186Sayznaga 	if (mc_dimm_sids)
2121186Sayznaga 		kmem_free(mc_dimm_sids, sizeof (dimm_sid_cache_t) *
2131186Sayznaga 		    max_entries);
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	return (0);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2190Sstevel@tonic-gate _info(struct modinfo *modinfop)
2200Sstevel@tonic-gate {
2210Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate static int
mc_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)2250Sstevel@tonic-gate mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2260Sstevel@tonic-gate {
2270Sstevel@tonic-gate 	struct mc_soft_state *softsp;
2280Sstevel@tonic-gate 	struct dimm_info *dimminfop;
2290Sstevel@tonic-gate 	int instance, len, err;
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	/* get the instance of this devi */
2320Sstevel@tonic-gate 	instance = ddi_get_instance(devi);
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	switch (cmd) {
2350Sstevel@tonic-gate 	case DDI_ATTACH:
2360Sstevel@tonic-gate 		break;
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	case DDI_RESUME:
2390Sstevel@tonic-gate 		/* get the soft state pointer for this device node */
2400Sstevel@tonic-gate 		softsp = ddi_get_soft_state(mcp, instance);
2410Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: DDI_RESUME: updating MADRs\n",
2420Sstevel@tonic-gate 		    instance));
2430Sstevel@tonic-gate 		/*
2440Sstevel@tonic-gate 		 * During resume, the source and target board's bank_infos
2450Sstevel@tonic-gate 		 * need to be updated with the new mc MADR values.  This is
2460Sstevel@tonic-gate 		 * implemented with existing functionality by first removing
2470Sstevel@tonic-gate 		 * the props and allocated data structs, and then adding them
2480Sstevel@tonic-gate 		 * back in.
2490Sstevel@tonic-gate 		 */
2500Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
2510Sstevel@tonic-gate 		    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
2520Sstevel@tonic-gate 		    MEM_CFG_PROP_NAME) == 1) {
2530Sstevel@tonic-gate 			(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
2540Sstevel@tonic-gate 			    MEM_CFG_PROP_NAME);
2550Sstevel@tonic-gate 		}
2561186Sayznaga 		mlayout_del(softsp->portid, 0);
2570Sstevel@tonic-gate 		if (mc_get_mcregs(softsp) == -1) {
2580Sstevel@tonic-gate 			cmn_err(CE_WARN, "mc_attach: mc%d DDI_RESUME failure\n",
2590Sstevel@tonic-gate 			    instance);
2600Sstevel@tonic-gate 		}
2610Sstevel@tonic-gate 		return (DDI_SUCCESS);
2620Sstevel@tonic-gate 
2630Sstevel@tonic-gate 	default:
2640Sstevel@tonic-gate 		return (DDI_FAILURE);
2650Sstevel@tonic-gate 	}
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(mcp, instance) != DDI_SUCCESS)
2680Sstevel@tonic-gate 		return (DDI_FAILURE);
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	softsp = ddi_get_soft_state(mcp, instance);
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	/* Set the dip in the soft state */
2730Sstevel@tonic-gate 	softsp->dip = devi;
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	if ((softsp->portid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
2760Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
2770Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to get %s property",
2780Sstevel@tonic-gate 		    instance, "portid"));
2790Sstevel@tonic-gate 		goto bad;
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	DPRINTF(MC_ATTACH_DEBUG, ("mc%d ATTACH: portid %d, cpuid %d\n",
2830Sstevel@tonic-gate 	    instance, softsp->portid, CPU->cpu_id));
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	/* map in the registers for this device. */
2860Sstevel@tonic-gate 	if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0)) {
2870Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to map registers",
2880Sstevel@tonic-gate 		    instance));
2890Sstevel@tonic-gate 		goto bad;
2900Sstevel@tonic-gate 	}
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	/*
2930Sstevel@tonic-gate 	 * Get the label of dimms and pin routing information at memory-layout
2940Sstevel@tonic-gate 	 * property if the memory controller is enabled.
2950Sstevel@tonic-gate 	 *
2960Sstevel@tonic-gate 	 * Basically every memory-controller node on every machine should
2970Sstevel@tonic-gate 	 * have one of these properties unless the memory controller is
2980Sstevel@tonic-gate 	 * physically not capable of having memory attached to it, e.g.
2990Sstevel@tonic-gate 	 * Excalibur's slave processor.
3000Sstevel@tonic-gate 	 */
3010Sstevel@tonic-gate 	err = ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip, DDI_PROP_DONTPASS,
3020Sstevel@tonic-gate 	    "memory-layout", (caddr_t)&dimminfop, &len);
3030Sstevel@tonic-gate 	if (err == DDI_PROP_SUCCESS) {
3040Sstevel@tonic-gate 		/*
3050Sstevel@tonic-gate 		 * Set the pointer and size of property in the soft state
3060Sstevel@tonic-gate 		 */
3070Sstevel@tonic-gate 		softsp->memlayoutp = dimminfop;
3080Sstevel@tonic-gate 		softsp->size = len;
3090Sstevel@tonic-gate 	} else if (err == DDI_PROP_NOT_FOUND) {
3100Sstevel@tonic-gate 		/*
3110Sstevel@tonic-gate 		 * This is a disable MC. Clear out the pointer and size
3120Sstevel@tonic-gate 		 * of property in the soft state
3130Sstevel@tonic-gate 		 */
3140Sstevel@tonic-gate 		softsp->memlayoutp = NULL;
3150Sstevel@tonic-gate 		softsp->size = 0;
3160Sstevel@tonic-gate 	} else {
3170Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d is disabled: dimminfop %p\n",
3187632SNick.Todd@Sun.COM 		    instance, (void *)dimminfop));
3190Sstevel@tonic-gate 		goto bad2;
3200Sstevel@tonic-gate 	}
3210Sstevel@tonic-gate 
322946Smathue 	DPRINTF(MC_ATTACH_DEBUG, ("mc%d: dimminfop=0x%p data=0x%lx len=%d\n",
3237632SNick.Todd@Sun.COM 	    instance, (void *)dimminfop, *(uint64_t *)dimminfop, len));
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	/* Get MC registers and construct all needed data structure */
3260Sstevel@tonic-gate 	if (mc_get_mcregs(softsp) == -1)
3270Sstevel@tonic-gate 		goto bad1;
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	mutex_enter(&mcmutex);
3300Sstevel@tonic-gate 	if (nmcs == 1) {
3310Sstevel@tonic-gate 		if (&p2get_mem_unum)
3320Sstevel@tonic-gate 			p2get_mem_unum = mc_get_mem_unum;
3330Sstevel@tonic-gate 		if (&p2get_mem_info)
3340Sstevel@tonic-gate 			p2get_mem_info = mc_get_mem_info;
3351186Sayznaga 		if (&p2get_mem_sid)
3361186Sayznaga 			p2get_mem_sid = mc_get_mem_sid;
3371186Sayznaga 		if (&p2get_mem_offset)
3381186Sayznaga 			p2get_mem_offset = mc_get_mem_offset;
3391186Sayznaga 		if (&p2get_mem_addr)
3401186Sayznaga 			p2get_mem_addr = mc_get_mem_addr;
3411186Sayznaga 		if (&p2init_sid_cache)
3421186Sayznaga 			p2init_sid_cache = mc_init_sid_cache;
3430Sstevel@tonic-gate 	}
3441186Sayznaga 
3450Sstevel@tonic-gate 	mutex_exit(&mcmutex);
3460Sstevel@tonic-gate 
3471186Sayznaga 	/*
3481186Sayznaga 	 * Update DIMM serial id information if the DIMM serial id
3491186Sayznaga 	 * cache has already been initialized.
3501186Sayznaga 	 */
3511186Sayznaga 	if (mc_dimm_sids) {
3521186Sayznaga 		rw_enter(&mcdimmsids_rw, RW_WRITER);
3531186Sayznaga 		(void) mc_populate_sid_cache();
3541186Sayznaga 		rw_exit(&mcdimmsids_rw);
3551186Sayznaga 	}
3561186Sayznaga 
3570Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "mc-us3", S_IFCHR, instance,
3580Sstevel@tonic-gate 	    "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
3590Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc_attach: create_minor_node"
3600Sstevel@tonic-gate 		    " failed \n"));
3610Sstevel@tonic-gate 		goto bad1;
3620Sstevel@tonic-gate 	}
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	ddi_report_dev(devi);
3650Sstevel@tonic-gate 	return (DDI_SUCCESS);
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate bad1:
3680Sstevel@tonic-gate 	/* release all allocated data struture for this MC */
3691186Sayznaga 	mlayout_del(softsp->portid, 0);
3700Sstevel@tonic-gate 	if (softsp->memlayoutp != NULL)
3710Sstevel@tonic-gate 		kmem_free(softsp->memlayoutp, softsp->size);
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 	/* remove the libdevinfo property */
3740Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
3750Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
3760Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) == 1) {
3770Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
3786803Spothier 		    MEM_CFG_PROP_NAME);
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate bad2:
3820Sstevel@tonic-gate 	/* unmap the registers for this device. */
3830Sstevel@tonic-gate 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate bad:
3860Sstevel@tonic-gate 	ddi_soft_state_free(mcp, instance);
3870Sstevel@tonic-gate 	return (DDI_FAILURE);
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate /* ARGSUSED */
3910Sstevel@tonic-gate static int
mc_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)3920Sstevel@tonic-gate mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3930Sstevel@tonic-gate {
3940Sstevel@tonic-gate 	int instance;
3950Sstevel@tonic-gate 	struct mc_soft_state *softsp;
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	/* get the instance of this devi */
3980Sstevel@tonic-gate 	instance = ddi_get_instance(devi);
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 	/* get the soft state pointer for this device node */
4010Sstevel@tonic-gate 	softsp = ddi_get_soft_state(mcp, instance);
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	switch (cmd) {
4040Sstevel@tonic-gate 	case DDI_SUSPEND:
4050Sstevel@tonic-gate 		return (DDI_SUCCESS);
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	case DDI_DETACH:
4080Sstevel@tonic-gate 		break;
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	default:
4110Sstevel@tonic-gate 		return (DDI_FAILURE);
4120Sstevel@tonic-gate 	}
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	DPRINTF(MC_DETACH_DEBUG, ("mc%d DETACH: portid= %d, table 0x%p\n",
4150Sstevel@tonic-gate 	    instance, softsp->portid, softsp->memlayoutp));
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate 	/* remove the libdevinfo property */
4180Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
4190Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
4200Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) == 1) {
4210Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
4226803Spothier 		    MEM_CFG_PROP_NAME);
4230Sstevel@tonic-gate 	}
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 	/* release all allocated data struture for this MC */
4261186Sayznaga 	mlayout_del(softsp->portid, 1);
4270Sstevel@tonic-gate 	if (softsp->memlayoutp != NULL)
4280Sstevel@tonic-gate 		kmem_free(softsp->memlayoutp, softsp->size);
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	/* unmap the registers */
4310Sstevel@tonic-gate 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	mutex_enter(&mcmutex);
4340Sstevel@tonic-gate 	if (nmcs == 0) {
4350Sstevel@tonic-gate 		if (&p2get_mem_unum)
4360Sstevel@tonic-gate 			p2get_mem_unum = NULL;
4370Sstevel@tonic-gate 		if (&p2get_mem_info)
4380Sstevel@tonic-gate 			p2get_mem_info = NULL;
4391186Sayznaga 		if (&p2get_mem_sid)
4401186Sayznaga 			p2get_mem_sid = NULL;
4411186Sayznaga 		if (&p2get_mem_offset)
4421186Sayznaga 			p2get_mem_offset = NULL;
4431186Sayznaga 		if (&p2get_mem_addr)
4441186Sayznaga 			p2get_mem_addr = NULL;
4451186Sayznaga 		if (&p2init_sid_cache)
4461186Sayznaga 			p2init_sid_cache = NULL;
4470Sstevel@tonic-gate 	}
4481186Sayznaga 
4490Sstevel@tonic-gate 	mutex_exit(&mcmutex);
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate 	/* free up the soft state */
4540Sstevel@tonic-gate 	ddi_soft_state_free(mcp, instance);
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	return (DDI_SUCCESS);
4570Sstevel@tonic-gate }
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate /* ARGSUSED */
4600Sstevel@tonic-gate static int
mc_open(dev_t * devp,int flag,int otyp,cred_t * credp)4610Sstevel@tonic-gate mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4620Sstevel@tonic-gate {
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	/* verify that otyp is appropriate */
4650Sstevel@tonic-gate 	if (otyp != OTYP_CHR) {
4660Sstevel@tonic-gate 		return (EINVAL);
4670Sstevel@tonic-gate 	}
4680Sstevel@tonic-gate 
4692953Smb158278 	return (0);
4700Sstevel@tonic-gate }
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate /* ARGSUSED */
4730Sstevel@tonic-gate static int
mc_close(dev_t devp,int flag,int otyp,cred_t * credp)4740Sstevel@tonic-gate mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
4750Sstevel@tonic-gate {
4760Sstevel@tonic-gate 	return (0);
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate /*
4800Sstevel@tonic-gate  * cmd includes MCIOC_MEMCONF, MCIOC_MEM, MCIOC_SEG, MCIOC_BANK, MCIOC_DEVGRP,
4810Sstevel@tonic-gate  * MCIOC_CTRLCONF, MCIOC_CONTROL.
4820Sstevel@tonic-gate  *
4830Sstevel@tonic-gate  * MCIOC_MEM, MCIOC_SEG, MCIOC_CTRLCONF, and MCIOC_CONTROL are
4840Sstevel@tonic-gate  * associated with various length struct. If given number is less than the
4850Sstevel@tonic-gate  * number in kernel, update the number and return EINVAL so that user could
4860Sstevel@tonic-gate  * allocate enough space for it.
4870Sstevel@tonic-gate  *
4880Sstevel@tonic-gate  */
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate /* ARGSUSED */
4910Sstevel@tonic-gate static int
mc_ioctl(dev_t dev,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)4920Sstevel@tonic-gate mc_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
4930Sstevel@tonic-gate 	int *rval_p)
4940Sstevel@tonic-gate {
4950Sstevel@tonic-gate 	size_t	size;
4960Sstevel@tonic-gate 	struct mc_memconf mcmconf;
4970Sstevel@tonic-gate 	struct mc_memory *mcmem, mcmem_in;
4980Sstevel@tonic-gate 	struct mc_segment *mcseg, mcseg_in;
4990Sstevel@tonic-gate 	struct mc_bank mcbank;
5000Sstevel@tonic-gate 	struct mc_devgrp mcdevgrp;
5010Sstevel@tonic-gate 	struct mc_ctrlconf *mcctrlconf, mcctrlconf_in;
5020Sstevel@tonic-gate 	struct mc_control *mccontrol, mccontrol_in;
5030Sstevel@tonic-gate 	struct seg_info *seg = NULL;
5040Sstevel@tonic-gate 	struct bank_info *bank = NULL;
5050Sstevel@tonic-gate 	struct dgrp_info *dgrp = NULL;
5060Sstevel@tonic-gate 	struct mctrl_info *mcport;
5070Sstevel@tonic-gate 	mc_dlist_t *mctrl;
5080Sstevel@tonic-gate 	int i, status = 0;
5090Sstevel@tonic-gate 	cpu_t *cpu;
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 	switch (cmd) {
5120Sstevel@tonic-gate 	case MCIOC_MEMCONF:
5130Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 		mcmconf.nmcs = nmcs;
5160Sstevel@tonic-gate 		mcmconf.nsegments = nsegments;
5170Sstevel@tonic-gate 		mcmconf.nbanks = maxbanks;
5180Sstevel@tonic-gate 		mcmconf.ndevgrps = NDGRPS;
5190Sstevel@tonic-gate 		mcmconf.ndevs = NDIMMS;
5200Sstevel@tonic-gate 		mcmconf.len_dev = MAX_DEVLEN;
5210Sstevel@tonic-gate 		mcmconf.xfer_size = TRANSFER_SIZE;
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 		if (copyout(&mcmconf, (void *)arg, sizeof (struct mc_memconf)))
5260Sstevel@tonic-gate 			return (EFAULT);
5270Sstevel@tonic-gate 		return (0);
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	/*
5300Sstevel@tonic-gate 	 * input: nsegments and allocate space for various length of segmentids
5310Sstevel@tonic-gate 	 *
5320Sstevel@tonic-gate 	 * return    0: size, number of segments, and all segment ids,
5330Sstevel@tonic-gate 	 *		where glocal and local ids are identical.
5340Sstevel@tonic-gate 	 *	EINVAL: if the given nsegments is less than that in kernel and
5350Sstevel@tonic-gate 	 *		nsegments of struct will be updated.
5360Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
5370Sstevel@tonic-gate 	 */
5380Sstevel@tonic-gate 	case MCIOC_MEM:
5390Sstevel@tonic-gate 		if (copyin((void *)arg, &mcmem_in,
5400Sstevel@tonic-gate 		    sizeof (struct mc_memory)) != 0)
5410Sstevel@tonic-gate 			return (EFAULT);
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
5440Sstevel@tonic-gate 		if (mcmem_in.nsegments < nsegments) {
5450Sstevel@tonic-gate 			mcmem_in.nsegments = nsegments;
5460Sstevel@tonic-gate 			if (copyout(&mcmem_in, (void *)arg,
5470Sstevel@tonic-gate 			    sizeof (struct mc_memory)))
5480Sstevel@tonic-gate 				status = EFAULT;
5490Sstevel@tonic-gate 			else
5500Sstevel@tonic-gate 				status = EINVAL;
5510Sstevel@tonic-gate 
5520Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
5530Sstevel@tonic-gate 			return (status);
5540Sstevel@tonic-gate 		}
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 		size = sizeof (struct mc_memory) + (nsegments - 1) *
5570Sstevel@tonic-gate 		    sizeof (mcmem->segmentids[0]);
5580Sstevel@tonic-gate 		mcmem = kmem_zalloc(size, KM_SLEEP);
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 		mcmem->size = memsize;
5610Sstevel@tonic-gate 		mcmem->nsegments = nsegments;
5620Sstevel@tonic-gate 		seg = (struct seg_info *)seg_head;
5630Sstevel@tonic-gate 		for (i = 0; i < nsegments; i++) {
5640Sstevel@tonic-gate 			ASSERT(seg != NULL);
5650Sstevel@tonic-gate 			mcmem->segmentids[i].globalid = seg->seg_node.id;
5660Sstevel@tonic-gate 			mcmem->segmentids[i].localid = seg->seg_node.id;
5670Sstevel@tonic-gate 			seg = (struct seg_info *)seg->seg_node.next;
5680Sstevel@tonic-gate 		}
5690Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 		if (copyout(mcmem, (void *)arg, size))
5720Sstevel@tonic-gate 			status = EFAULT;
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 		kmem_free(mcmem, size);
5750Sstevel@tonic-gate 		return (status);
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	/*
5780Sstevel@tonic-gate 	 * input: id, nbanks and allocate space for various length of bankids
5790Sstevel@tonic-gate 	 *
5800Sstevel@tonic-gate 	 * return    0: base, size, number of banks, and all bank ids,
5810Sstevel@tonic-gate 	 *		where global id is unique of all banks and local id
5820Sstevel@tonic-gate 	 *		is only unique for mc.
5830Sstevel@tonic-gate 	 *	EINVAL: either id isn't found or if given nbanks is less than
5840Sstevel@tonic-gate 	 *		that in kernel and nbanks of struct will be updated.
5850Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
5860Sstevel@tonic-gate 	 */
5870Sstevel@tonic-gate 	case MCIOC_SEG:
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 		if (copyin((void *)arg, &mcseg_in,
5900Sstevel@tonic-gate 		    sizeof (struct mc_segment)) != 0)
5910Sstevel@tonic-gate 			return (EFAULT);
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
5940Sstevel@tonic-gate 		if ((seg = (struct seg_info *)mc_node_get(mcseg_in.id,
5950Sstevel@tonic-gate 		    seg_head)) == NULL) {
5960Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG: seg not match, "
5970Sstevel@tonic-gate 			    "id %d\n", mcseg_in.id));
5980Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
5990Sstevel@tonic-gate 			return (EFAULT);
6000Sstevel@tonic-gate 		}
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 		if (mcseg_in.nbanks < seg->nbanks) {
6030Sstevel@tonic-gate 			mcseg_in.nbanks = seg->nbanks;
6040Sstevel@tonic-gate 			if (copyout(&mcseg_in, (void *)arg,
6050Sstevel@tonic-gate 			    sizeof (struct mc_segment)))
6060Sstevel@tonic-gate 				status = EFAULT;
6070Sstevel@tonic-gate 			else
6080Sstevel@tonic-gate 				status = EINVAL;
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
6110Sstevel@tonic-gate 			return (status);
6120Sstevel@tonic-gate 		}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 		size = sizeof (struct mc_segment) + (seg->nbanks - 1) *
6150Sstevel@tonic-gate 		    sizeof (mcseg->bankids[0]);
6160Sstevel@tonic-gate 		mcseg = kmem_zalloc(size, KM_SLEEP);
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 		mcseg->id = seg->seg_node.id;
6190Sstevel@tonic-gate 		mcseg->ifactor = seg->ifactor;
6200Sstevel@tonic-gate 		mcseg->base = seg->base;
6210Sstevel@tonic-gate 		mcseg->size = seg->size;
6220Sstevel@tonic-gate 		mcseg->nbanks = seg->nbanks;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 		bank = seg->hb_inseg;
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:nbanks %d seg 0x%p bank %p\n",
6277632SNick.Todd@Sun.COM 		    seg->nbanks, (void *)seg, (void *)bank));
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate 		i = 0;
6300Sstevel@tonic-gate 		while (bank != NULL) {
6310Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:idx %d bank_id %d\n",
6320Sstevel@tonic-gate 			    i, bank->bank_node.id));
6330Sstevel@tonic-gate 			mcseg->bankids[i].globalid = bank->bank_node.id;
6340Sstevel@tonic-gate 			mcseg->bankids[i++].localid =
6350Sstevel@tonic-gate 			    bank->local_id;
6360Sstevel@tonic-gate 			bank = bank->n_inseg;
6370Sstevel@tonic-gate 		}
6380Sstevel@tonic-gate 		ASSERT(i == seg->nbanks);
6390Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 		if (copyout(mcseg, (void *)arg, size))
6420Sstevel@tonic-gate 			status = EFAULT;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 		kmem_free(mcseg, size);
6450Sstevel@tonic-gate 		return (status);
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate 	/*
6480Sstevel@tonic-gate 	 * input: id
6490Sstevel@tonic-gate 	 *
6500Sstevel@tonic-gate 	 * return    0: mask, match, size, and devgrpid,
6510Sstevel@tonic-gate 	 *		where global id is unique of all devgrps and local id
6520Sstevel@tonic-gate 	 *		is only unique for mc.
6530Sstevel@tonic-gate 	 *	EINVAL: if id isn't found
6540Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
6550Sstevel@tonic-gate 	 */
6560Sstevel@tonic-gate 	case MCIOC_BANK:
6570Sstevel@tonic-gate 		if (copyin((void *)arg, &mcbank, sizeof (struct mc_bank)) != 0)
6580Sstevel@tonic-gate 			return (EFAULT);
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank id %d\n", mcbank.id));
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 		if ((bank = (struct bank_info *)mc_node_get(mcbank.id,
6650Sstevel@tonic-gate 		    bank_head)) == NULL) {
6660Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
6670Sstevel@tonic-gate 			return (EINVAL);
6680Sstevel@tonic-gate 		}
6690Sstevel@tonic-gate 
670946Smathue 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank %d (0x%p) valid %hu\n",
6717632SNick.Todd@Sun.COM 		    bank->bank_node.id, (void *)bank, bank->valid));
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate 		/*
6740Sstevel@tonic-gate 		 * If (Physic Address & MASK) == MATCH, Physic Address is
6750Sstevel@tonic-gate 		 * located at this bank. The lower physical address bits
6760Sstevel@tonic-gate 		 * are at [9-6].
6770Sstevel@tonic-gate 		 */
6780Sstevel@tonic-gate 		mcbank.mask = (~(bank->lk | ~(MADR_LK_MASK >>
6790Sstevel@tonic-gate 		    MADR_LK_SHIFT))) << MADR_LPA_SHIFT;
6800Sstevel@tonic-gate 		mcbank.match = bank->lm << MADR_LPA_SHIFT;
6810Sstevel@tonic-gate 		mcbank.size = bank->size;
6820Sstevel@tonic-gate 		mcbank.devgrpid.globalid = bank->devgrp_id;
6830Sstevel@tonic-gate 		mcbank.devgrpid.localid = bank->devgrp_id % NDGRPS;
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 		if (copyout(&mcbank, (void *)arg, sizeof (struct mc_bank)))
6880Sstevel@tonic-gate 			return (EFAULT);
6890Sstevel@tonic-gate 		return (0);
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 	/*
6920Sstevel@tonic-gate 	 * input:id and allocate space for various length of deviceids
6930Sstevel@tonic-gate 	 *
6940Sstevel@tonic-gate 	 * return    0: size and number of devices.
6950Sstevel@tonic-gate 	 *	EINVAL: id isn't found
6960Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
6970Sstevel@tonic-gate 	 */
6980Sstevel@tonic-gate 	case MCIOC_DEVGRP:
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 		if (copyin((void *)arg, &mcdevgrp,
7010Sstevel@tonic-gate 		    sizeof (struct mc_devgrp)) != 0)
7020Sstevel@tonic-gate 			return (EFAULT);
7030Sstevel@tonic-gate 
7040Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
7050Sstevel@tonic-gate 		if ((dgrp = (struct dgrp_info *)mc_node_get(mcdevgrp.id,
7060Sstevel@tonic-gate 		    dgrp_head)) == NULL) {
7070Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_DEVGRP: not match, id "
7080Sstevel@tonic-gate 			    "%d\n", mcdevgrp.id));
7090Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
7100Sstevel@tonic-gate 			return (EINVAL);
7110Sstevel@tonic-gate 		}
7120Sstevel@tonic-gate 
7130Sstevel@tonic-gate 		mcdevgrp.ndevices = dgrp->ndevices;
7140Sstevel@tonic-gate 		mcdevgrp.size = dgrp->size;
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate 		if (copyout(&mcdevgrp, (void *)arg, sizeof (struct mc_devgrp)))
7190Sstevel@tonic-gate 			status = EFAULT;
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate 		return (status);
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 	/*
7240Sstevel@tonic-gate 	 * input: nmcs and allocate space for various length of mcids
7250Sstevel@tonic-gate 	 *
7260Sstevel@tonic-gate 	 * return    0: number of mc, and all mcids,
7270Sstevel@tonic-gate 	 *		where glocal and local ids are identical.
7280Sstevel@tonic-gate 	 *	EINVAL: if the given nmcs is less than that in kernel and
7290Sstevel@tonic-gate 	 *		nmcs of struct will be updated.
7300Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
7310Sstevel@tonic-gate 	 */
7320Sstevel@tonic-gate 	case MCIOC_CTRLCONF:
7330Sstevel@tonic-gate 		if (copyin((void *)arg, &mcctrlconf_in,
7340Sstevel@tonic-gate 		    sizeof (struct mc_ctrlconf)) != 0)
7350Sstevel@tonic-gate 			return (EFAULT);
7360Sstevel@tonic-gate 
7370Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
7380Sstevel@tonic-gate 		if (mcctrlconf_in.nmcs < nmcs) {
7390Sstevel@tonic-gate 			mcctrlconf_in.nmcs = nmcs;
7400Sstevel@tonic-gate 			if (copyout(&mcctrlconf_in, (void *)arg,
7410Sstevel@tonic-gate 			    sizeof (struct mc_ctrlconf)))
7420Sstevel@tonic-gate 				status = EFAULT;
7430Sstevel@tonic-gate 			else
7440Sstevel@tonic-gate 				status = EINVAL;
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
7470Sstevel@tonic-gate 			return (status);
7480Sstevel@tonic-gate 		}
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 		/*
7510Sstevel@tonic-gate 		 * Cannot just use the size of the struct because of the various
7520Sstevel@tonic-gate 		 * length struct
7530Sstevel@tonic-gate 		 */
7540Sstevel@tonic-gate 		size = sizeof (struct mc_ctrlconf) + ((nmcs - 1) *
7550Sstevel@tonic-gate 		    sizeof (mcctrlconf->mcids[0]));
7560Sstevel@tonic-gate 		mcctrlconf = kmem_zalloc(size, KM_SLEEP);
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 		mcctrlconf->nmcs = nmcs;
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate 		/* Get all MC ids and add to mcctrlconf */
7610Sstevel@tonic-gate 		mctrl = mctrl_head;
7620Sstevel@tonic-gate 		i = 0;
7630Sstevel@tonic-gate 		while (mctrl != NULL) {
7640Sstevel@tonic-gate 			mcctrlconf->mcids[i].globalid = mctrl->id;
7650Sstevel@tonic-gate 			mcctrlconf->mcids[i].localid = mctrl->id;
7660Sstevel@tonic-gate 			i++;
7670Sstevel@tonic-gate 			mctrl = mctrl->next;
7680Sstevel@tonic-gate 		}
7690Sstevel@tonic-gate 		ASSERT(i == nmcs);
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 		if (copyout(mcctrlconf, (void *)arg, size))
7740Sstevel@tonic-gate 			status = EFAULT;
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 		kmem_free(mcctrlconf, size);
7770Sstevel@tonic-gate 		return (status);
7780Sstevel@tonic-gate 
7790Sstevel@tonic-gate 	/*
7800Sstevel@tonic-gate 	 * input:id, ndevgrps and allocate space for various length of devgrpids
7810Sstevel@tonic-gate 	 *
7820Sstevel@tonic-gate 	 * return    0: number of devgrp, and all devgrpids,
7830Sstevel@tonic-gate 	 *		is unique of all devgrps and local id is only unique
7840Sstevel@tonic-gate 	 *		for mc.
7850Sstevel@tonic-gate 	 *	EINVAL: either if id isn't found or if the given ndevgrps is
7860Sstevel@tonic-gate 	 *		less than that in kernel and ndevgrps of struct will
7870Sstevel@tonic-gate 	 *		be updated.
7880Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
7890Sstevel@tonic-gate 	 */
7900Sstevel@tonic-gate 	case MCIOC_CONTROL:
7910Sstevel@tonic-gate 		if (copyin((void *)arg, &mccontrol_in,
7920Sstevel@tonic-gate 		    sizeof (struct mc_control)) != 0)
7930Sstevel@tonic-gate 			return (EFAULT);
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
7960Sstevel@tonic-gate 		if ((mcport = (struct mctrl_info *)mc_node_get(mccontrol_in.id,
7970Sstevel@tonic-gate 		    mctrl_head)) == NULL) {
7980Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
7990Sstevel@tonic-gate 			return (EINVAL);
8000Sstevel@tonic-gate 		}
8010Sstevel@tonic-gate 
8020Sstevel@tonic-gate 		/*
8030Sstevel@tonic-gate 		 * mcport->ndevgrps zero means Memory Controller is disable.
8040Sstevel@tonic-gate 		 */
8050Sstevel@tonic-gate 		if ((mccontrol_in.ndevgrps < mcport->ndevgrps) ||
8060Sstevel@tonic-gate 		    (mcport->ndevgrps == 0)) {
8070Sstevel@tonic-gate 			mccontrol_in.ndevgrps = mcport->ndevgrps;
8080Sstevel@tonic-gate 			if (copyout(&mccontrol_in, (void *)arg,
8090Sstevel@tonic-gate 			    sizeof (struct mc_control)))
8100Sstevel@tonic-gate 				status = EFAULT;
8110Sstevel@tonic-gate 			else if (mcport->ndevgrps != 0)
8120Sstevel@tonic-gate 				status = EINVAL;
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
8150Sstevel@tonic-gate 			return (status);
8160Sstevel@tonic-gate 		}
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 		size = sizeof (struct mc_control) + (mcport->ndevgrps - 1) *
8190Sstevel@tonic-gate 		    sizeof (mccontrol->devgrpids[0]);
8200Sstevel@tonic-gate 		mccontrol = kmem_zalloc(size, KM_SLEEP);
8210Sstevel@tonic-gate 
8220Sstevel@tonic-gate 		mccontrol->id = mcport->mctrl_node.id;
8230Sstevel@tonic-gate 		mccontrol->ndevgrps = mcport->ndevgrps;
8240Sstevel@tonic-gate 		for (i = 0; i < mcport->ndevgrps; i++) {
8250Sstevel@tonic-gate 			mccontrol->devgrpids[i].globalid = mcport->devgrpids[i];
8260Sstevel@tonic-gate 			mccontrol->devgrpids[i].localid =
8270Sstevel@tonic-gate 			    mcport->devgrpids[i] % NDGRPS;
828946Smathue 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_CONTROL: devgrp id %lu\n",
829946Smathue 			    *(uint64_t *)&mccontrol->devgrpids[i]));
8300Sstevel@tonic-gate 		}
8310Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
8320Sstevel@tonic-gate 
8330Sstevel@tonic-gate 		if (copyout(mccontrol, (void *)arg, size))
8340Sstevel@tonic-gate 			status = EFAULT;
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 		kmem_free(mccontrol, size);
8370Sstevel@tonic-gate 		return (status);
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 	/*
8400Sstevel@tonic-gate 	 * input:id
8410Sstevel@tonic-gate 	 *
8420Sstevel@tonic-gate 	 * return    0: CPU flushed successfully.
8430Sstevel@tonic-gate 	 *	EINVAL: the id wasn't found
8440Sstevel@tonic-gate 	 */
8450Sstevel@tonic-gate 	case MCIOC_ECFLUSH:
8460Sstevel@tonic-gate 		mutex_enter(&cpu_lock);
8470Sstevel@tonic-gate 		cpu = cpu_get((processorid_t)arg);
8480Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
8490Sstevel@tonic-gate 		if (cpu == NULL)
8500Sstevel@tonic-gate 			return (EINVAL);
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 		xc_one(arg, (xcfunc_t *)cpu_flush_ecache, 0, 0);
8530Sstevel@tonic-gate 
8540Sstevel@tonic-gate 		return (0);
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate 	default:
8570Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("DEFAULT: cmd is wrong\n"));
8580Sstevel@tonic-gate 		return (EFAULT);
8590Sstevel@tonic-gate 	}
8600Sstevel@tonic-gate }
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate /*
8630Sstevel@tonic-gate  * Get Memory Address Decoding Registers and construct list.
8640Sstevel@tonic-gate  * flag is to workaround Cheetah's restriction where register cannot be mapped
8650Sstevel@tonic-gate  * if port id(MC registers on it) == cpu id(process is running on it).
8660Sstevel@tonic-gate  */
8670Sstevel@tonic-gate static int
mc_get_mcregs(struct mc_soft_state * softsp)8680Sstevel@tonic-gate mc_get_mcregs(struct mc_soft_state *softsp)
8690Sstevel@tonic-gate {
8700Sstevel@tonic-gate 	int i;
8710Sstevel@tonic-gate 	int err = 0;
8720Sstevel@tonic-gate 	uint64_t madreg;
8730Sstevel@tonic-gate 	uint64_t ma_reg_array[NBANKS];	/* there are NBANKS of madrs */
8740Sstevel@tonic-gate 
8750Sstevel@tonic-gate 	/* Construct lists for MC, mctrl_info, dgrp_info, and device_info */
8760Sstevel@tonic-gate 	mc_construct(softsp->portid, softsp->memlayoutp);
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	/*
8790Sstevel@tonic-gate 	 * If memlayoutp is NULL, the Memory Controller is disable, and
8800Sstevel@tonic-gate 	 * doesn't need to create any bank and segment.
8810Sstevel@tonic-gate 	 */
8820Sstevel@tonic-gate 	if (softsp->memlayoutp == NULL)
8830Sstevel@tonic-gate 		goto exit;
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	/*
8860Sstevel@tonic-gate 	 * Get the content of 4 Memory Address Decoding Registers, and
8870Sstevel@tonic-gate 	 * construct lists of logical banks and segments.
8880Sstevel@tonic-gate 	 */
8890Sstevel@tonic-gate 	for (i = 0; i < NBANKS; i++) {
8900Sstevel@tonic-gate 		DPRINTF(MC_REG_DEBUG, ("get_mcregs: mapreg=0x%p portid=%d "
8917632SNick.Todd@Sun.COM 		    "cpu=%d\n", (void *)softsp->mc_base, softsp->portid,
8927632SNick.Todd@Sun.COM 		    CPU->cpu_id));
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 		kpreempt_disable();
8950Sstevel@tonic-gate 		if (softsp->portid == (cpunodes[CPU->cpu_id].portid))
8960Sstevel@tonic-gate 			madreg = get_mcr(MADR0OFFSET + (i * REGOFFSET));
8970Sstevel@tonic-gate 		else
8980Sstevel@tonic-gate 			madreg = *((uint64_t *)(softsp->mc_base + MADR0OFFSET +
8990Sstevel@tonic-gate 			    (i * REGOFFSET)));
9000Sstevel@tonic-gate 		kpreempt_enable();
9010Sstevel@tonic-gate 
9020Sstevel@tonic-gate 		DPRINTF(MC_REG_DEBUG, ("get_mcregs 2: memlayoutp=0x%p madreg "
903946Smathue 		    "reg=0x%lx\n", softsp->memlayoutp, madreg));
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 		ma_reg_array[i] = madreg;
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate 		if ((err = mlayout_add(softsp->portid, i, madreg,
9080Sstevel@tonic-gate 		    softsp->memlayoutp)) == -1)
9090Sstevel@tonic-gate 			break;
9100Sstevel@tonic-gate 	}
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	/*
9130Sstevel@tonic-gate 	 * Create the logical bank property for this mc node. This
9140Sstevel@tonic-gate 	 * property is an encoded array of the madr for each logical
9150Sstevel@tonic-gate 	 * bank (there are NBANKS of these).
9160Sstevel@tonic-gate 	 */
9170Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
9180Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
9190Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) != 1) {
9200Sstevel@tonic-gate 		(void) ddi_prop_create(DDI_DEV_T_NONE, softsp->dip,
9216803Spothier 		    DDI_PROP_CANSLEEP, MEM_CFG_PROP_NAME,
9226803Spothier 		    (caddr_t)&ma_reg_array, sizeof (ma_reg_array));
9230Sstevel@tonic-gate 	}
9240Sstevel@tonic-gate 
9250Sstevel@tonic-gate exit:
9260Sstevel@tonic-gate 	if (!err) {
9270Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
9280Sstevel@tonic-gate 		nmcs++;
9290Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
9300Sstevel@tonic-gate 	}
9310Sstevel@tonic-gate 	return (err);
9320Sstevel@tonic-gate }
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate /*
9351186Sayznaga  * Translate a <DIMM, offset> pair to a physical address.
9361186Sayznaga  */
9371186Sayznaga static int
mc_offset_to_addr(struct seg_info * seg,struct bank_info * bank,uint64_t off,uint64_t * addr)9381186Sayznaga mc_offset_to_addr(struct seg_info *seg,
9391186Sayznaga     struct bank_info *bank, uint64_t off, uint64_t *addr)
9401186Sayznaga {
9411186Sayznaga 	uint64_t base, size, line, remainder;
9421186Sayznaga 	uint32_t ifactor;
9431186Sayznaga 
9441186Sayznaga 	/*
9451186Sayznaga 	 * Compute the half-dimm size in bytes.
9461186Sayznaga 	 * Note that bank->size represents the number of data bytes,
9471186Sayznaga 	 * and does not include the additional bits used for ecc, mtag,
9481186Sayznaga 	 * and mtag ecc information in each 144-bit checkword.
9491186Sayznaga 	 * For calculating the offset to a checkword we need the size
9501186Sayznaga 	 * including the additional 8 bytes for each 64 data bytes of
9511186Sayznaga 	 * a cache line.
9521186Sayznaga 	 */
9531186Sayznaga 	size = ((bank->size / 4) / 64) * 72;
9541186Sayznaga 
9551186Sayznaga 	/*
9561186Sayznaga 	 * Check if the offset is within this bank. This depends on the position
9571186Sayznaga 	 * of the bank, i.e., whether it is the front bank or the back bank.
9581186Sayznaga 	 */
9591186Sayznaga 	base = size * bank->pos;
9601186Sayznaga 
9611186Sayznaga 	if ((off < base) || (off >= (base + size)))
9621186Sayznaga 		return (-1);
9631186Sayznaga 
9641186Sayznaga 	/*
9651186Sayznaga 	 * Compute the offset within the half-dimm.
9661186Sayznaga 	 */
9671186Sayznaga 	off -= base;
9681186Sayznaga 
9691186Sayznaga 	/*
9701186Sayznaga 	 * Compute the line within the half-dimm. This is the same as the line
9711186Sayznaga 	 * within the bank since each DIMM in a bank contributes uniformly
9721186Sayznaga 	 * 144 bits (18 bytes) to a cache line.
9731186Sayznaga 	 */
9741186Sayznaga 	line = off / QWORD_SIZE_BYTES;
9751186Sayznaga 
9761186Sayznaga 	remainder = off % QWORD_SIZE_BYTES;
9771186Sayznaga 
9781186Sayznaga 	/*
9791186Sayznaga 	 * Compute the line within the segment.
9801186Sayznaga 	 * The bank->lm field indicates the order in which cache lines are
9811186Sayznaga 	 * distributed across the banks of a segment (See the Cheetah PRM).
9821186Sayznaga 	 * The interleave factor the bank is programmed with is used instead
9831186Sayznaga 	 * of the segment interleave factor since a segment can be composed
9841186Sayznaga 	 * of banks with different interleave factors if the banks are not
9851186Sayznaga 	 * uniform in size.
9861186Sayznaga 	 */
9871186Sayznaga 	ifactor = (bank->lk ^ 0xF) + 1;
9881186Sayznaga 	line = (line * ifactor) + bank->lm;
9891186Sayznaga 
9901186Sayznaga 	/*
9911186Sayznaga 	 * Compute the physical address assuming that there are 64 data bytes
9921186Sayznaga 	 * in a cache line.
9931186Sayznaga 	 */
9941186Sayznaga 	*addr = (line << 6) + seg->base;
9951186Sayznaga 	*addr += remainder * 16;
9961186Sayznaga 
9971186Sayznaga 	return (0);
9981186Sayznaga }
9991186Sayznaga 
10001186Sayznaga /*
10011186Sayznaga  * Translate a physical address to a <DIMM, offset> pair.
10021186Sayznaga  */
10031186Sayznaga static void
mc_addr_to_offset(struct seg_info * seg,struct bank_info * bank,uint64_t addr,uint64_t * off)10041186Sayznaga mc_addr_to_offset(struct seg_info *seg,
10051186Sayznaga     struct bank_info *bank, uint64_t addr, uint64_t *off)
10061186Sayznaga {
10071186Sayznaga 	uint64_t base, size, line, remainder;
10081186Sayznaga 	uint32_t ifactor;
10091186Sayznaga 
10101186Sayznaga 	/*
10111186Sayznaga 	 * Compute the line within the segment assuming that there are 64 data
10121186Sayznaga 	 * bytes in a cache line.
10131186Sayznaga 	 */
10141186Sayznaga 	line = (addr - seg->base) / 64;
10151186Sayznaga 
10161186Sayznaga 	/*
10171186Sayznaga 	 * The lm (lower match) field from the Memory Address Decoding Register
10181186Sayznaga 	 * for this bank determines which lines within a memory segment this
10191186Sayznaga 	 * bank should respond to.  These are the actual address bits the
10201186Sayznaga 	 * interleave is done over (See the Cheetah PRM).
10211186Sayznaga 	 * In other words, the lm field indicates the order in which the cache
10221186Sayznaga 	 * lines are distributed across the banks of a segment, and thusly it
10231186Sayznaga 	 * can be used to compute the line within this bank. This is the same as
10241186Sayznaga 	 * the line within the half-dimm. This is because each DIMM in a bank
10251186Sayznaga 	 * contributes uniformly to every cache line.
10261186Sayznaga 	 */
10271186Sayznaga 	ifactor = (bank->lk ^ 0xF) + 1;
10281186Sayznaga 	line = (line - bank->lm)/ifactor;
10291186Sayznaga 
10301186Sayznaga 	/*
10311186Sayznaga 	 * Compute the offset within the half-dimm. This depends on whether
10321186Sayznaga 	 * or not the bank is a front logical bank or a back logical bank.
10331186Sayznaga 	 */
10341186Sayznaga 	*off = line * QWORD_SIZE_BYTES;
10351186Sayznaga 
10361186Sayznaga 	/*
10371186Sayznaga 	 * Compute the half-dimm size in bytes.
10381186Sayznaga 	 * Note that bank->size represents the number of data bytes,
10391186Sayznaga 	 * and does not include the additional bits used for ecc, mtag,
10401186Sayznaga 	 * and mtag ecc information in each 144-bit quadword.
10411186Sayznaga 	 * For calculating the offset to a checkword we need the size
10421186Sayznaga 	 * including the additional 8 bytes for each 64 data bytes of
10431186Sayznaga 	 * a cache line.
10441186Sayznaga 	 */
10451186Sayznaga 	size = ((bank->size / 4) / 64) * 72;
10461186Sayznaga 
10471186Sayznaga 	/*
10481186Sayznaga 	 * Compute the offset within the dimm to the nearest line. This depends
10491186Sayznaga 	 * on whether or not the bank is a front logical bank or a back logical
10501186Sayznaga 	 * bank.
10511186Sayznaga 	 */
10521186Sayznaga 	base = size * bank->pos;
10531186Sayznaga 	*off += base;
10541186Sayznaga 
10551186Sayznaga 	remainder = (addr - seg->base) % 64;
10561186Sayznaga 	remainder /= 16;
10571186Sayznaga 	*off += remainder;
10581186Sayznaga }
10591186Sayznaga 
10601186Sayznaga /*
10610Sstevel@tonic-gate  * A cache line is composed of four quadwords with the associated ECC, the
10620Sstevel@tonic-gate  * MTag along with its associated ECC. This is depicted below:
10630Sstevel@tonic-gate  *
10640Sstevel@tonic-gate  * |                    Data                    |   ECC   | Mtag |MTag ECC|
10650Sstevel@tonic-gate  *  127                                         0 8       0 2    0 3      0
10660Sstevel@tonic-gate  *
10670Sstevel@tonic-gate  * synd_code will be mapped as the following order to mc_get_mem_unum.
10680Sstevel@tonic-gate  *  143                                         16        7      4        0
10690Sstevel@tonic-gate  *
10700Sstevel@tonic-gate  * |  Quadword  0  |  Quadword  1  |  Quadword  2  |  Quadword  3  |
10710Sstevel@tonic-gate  *  575         432 431         288 287         144 143		   0
10720Sstevel@tonic-gate  *
10730Sstevel@tonic-gate  * dimm table: each bit at a cache line needs two bits to present one of
10740Sstevel@tonic-gate  *      four dimms. So it needs 144 bytes(576 * 2 / 8). The content is in
10750Sstevel@tonic-gate  *      big edian order, i.e. dimm_table[0] presents for bit 572 to 575.
10760Sstevel@tonic-gate  *
10770Sstevel@tonic-gate  * pin table: each bit at a cache line needs one byte to present pin position,
10780Sstevel@tonic-gate  *      where max. is 230. So it needs 576 bytes. The order of table index is
10790Sstevel@tonic-gate  *      the same as bit position at a cache line, i.e. pin_table[0] presents
10800Sstevel@tonic-gate  *      for bit 0, Mtag ECC 0 of Quadword 3.
10810Sstevel@tonic-gate  *
10820Sstevel@tonic-gate  * This is a mapping from syndrome code to QuadWord Logical layout at Safari.
10830Sstevel@tonic-gate  * Referring to Figure 3-4, Excalibur Architecture Manual.
10840Sstevel@tonic-gate  * This table could be moved to cheetah.c if other platform teams agree with
10850Sstevel@tonic-gate  * the bit layout at QuadWord.
10860Sstevel@tonic-gate  */
10870Sstevel@tonic-gate 
10880Sstevel@tonic-gate static uint8_t qwordmap[] =
10890Sstevel@tonic-gate {
10900Sstevel@tonic-gate 16,   17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
10910Sstevel@tonic-gate 32,   33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
10920Sstevel@tonic-gate 48,   49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
10930Sstevel@tonic-gate 64,   65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
10940Sstevel@tonic-gate 80,   81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
10950Sstevel@tonic-gate 96,   97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
10960Sstevel@tonic-gate 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
10970Sstevel@tonic-gate 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
10980Sstevel@tonic-gate 7,    8,   9,  10,  11,  12,  13,  14,  15,   4,   5,   6,   0,   1,   2,   3,
10990Sstevel@tonic-gate };
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate 
11020Sstevel@tonic-gate /* ARGSUSED */
11030Sstevel@tonic-gate static int
mc_get_mem_unum(int synd_code,uint64_t paddr,char * buf,int buflen,int * lenp)11040Sstevel@tonic-gate mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf, int buflen, int *lenp)
11050Sstevel@tonic-gate {
11060Sstevel@tonic-gate 	int i, upper_pa, lower_pa, dimmoffset;
11070Sstevel@tonic-gate 	int quadword, pos_cacheline, position, index, idx4dimm;
11080Sstevel@tonic-gate 	int qwlayout = synd_code;
11090Sstevel@tonic-gate 	short offset, data;
11100Sstevel@tonic-gate 	char unum[UNUM_NAMLEN];
11110Sstevel@tonic-gate 	struct dimm_info *dimmp;
11120Sstevel@tonic-gate 	struct pin_info *pinp;
11130Sstevel@tonic-gate 	struct bank_info *bank;
11140Sstevel@tonic-gate 
11150Sstevel@tonic-gate 	/*
11160Sstevel@tonic-gate 	 * Enforce old Openboot requirement for synd code, either a single-bit
11170Sstevel@tonic-gate 	 * code from 0..QWORD_SIZE-1 or -1 (multi-bit error).
11180Sstevel@tonic-gate 	 */
11190Sstevel@tonic-gate 	if (qwlayout < -1 || qwlayout >= QWORD_SIZE)
11200Sstevel@tonic-gate 		return (EINVAL);
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	unum[0] = '\0';
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
11250Sstevel@tonic-gate 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate 	DPRINTF(MC_GUNUM_DEBUG, ("qwlayout %d\n", qwlayout));
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate 	/*
11300Sstevel@tonic-gate 	 * Scan all logical banks to get one responding to the physical
11310Sstevel@tonic-gate 	 * address. Then compute the index to look up dimm and pin tables
11321186Sayznaga 	 * to generate the unum.
11330Sstevel@tonic-gate 	 */
11340Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
11350Sstevel@tonic-gate 	bank = (struct bank_info *)bank_head;
11360Sstevel@tonic-gate 	while (bank != NULL) {
11370Sstevel@tonic-gate 		int bankid, mcid, bankno_permc;
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate 		bankid = bank->bank_node.id;
11400Sstevel@tonic-gate 		bankno_permc = bankid % NBANKS;
11410Sstevel@tonic-gate 		mcid = bankid / NBANKS;
11420Sstevel@tonic-gate 
11430Sstevel@tonic-gate 		/*
11440Sstevel@tonic-gate 		 * The Address Decoding logic decodes the different fields
11451186Sayznaga 		 * in the Memory Address Decoding register to determine
11461186Sayznaga 		 * whether a particular logical bank should respond to a
11470Sstevel@tonic-gate 		 * physical address.
11480Sstevel@tonic-gate 		 */
11490Sstevel@tonic-gate 		if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) |
11500Sstevel@tonic-gate 		    bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) {
11510Sstevel@tonic-gate 			bank = (struct bank_info *)bank->bank_node.next;
11520Sstevel@tonic-gate 			continue;
11530Sstevel@tonic-gate 		}
11540Sstevel@tonic-gate 
11550Sstevel@tonic-gate 		dimmoffset = (bankno_permc % NDGRPS) * NDIMMS;
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate 		dimmp = (struct dimm_info *)bank->dimminfop;
11580Sstevel@tonic-gate 		ASSERT(dimmp != NULL);
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate 		if ((qwlayout >= 0) && (qwlayout < QWORD_SIZE)) {
11610Sstevel@tonic-gate 			/*
11620Sstevel@tonic-gate 			 * single-bit error handling, we can identify specific
11630Sstevel@tonic-gate 			 * DIMM.
11640Sstevel@tonic-gate 			 */
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 			pinp = (struct pin_info *)&dimmp->data[0];
11670Sstevel@tonic-gate 
11680Sstevel@tonic-gate 			if (!dimmp->sym_flag)
11690Sstevel@tonic-gate 				pinp++;
11700Sstevel@tonic-gate 
11710Sstevel@tonic-gate 			quadword = (paddr & 0x3f) / 16;
11720Sstevel@tonic-gate 			/* or quadword = (paddr >> 4) % 4; */
11731186Sayznaga 			pos_cacheline = ((3 - quadword) * QWORD_SIZE) +
11740Sstevel@tonic-gate 			    qwordmap[qwlayout];
11750Sstevel@tonic-gate 			position = 575 - pos_cacheline;
11760Sstevel@tonic-gate 			index = position * 2 / 8;
11770Sstevel@tonic-gate 			offset = position % 4;
11780Sstevel@tonic-gate 
11790Sstevel@tonic-gate 			/*
11801186Sayznaga 			 * Trade-off: We couldn't add pin number to
11811186Sayznaga 			 * unum string because statistic number
11820Sstevel@tonic-gate 			 * pumps up at the corresponding dimm not pin.
11830Sstevel@tonic-gate 			 * (void) sprintf(unum, "Pin %1u ", (uint_t)
11840Sstevel@tonic-gate 			 * pinp->pintable[pos_cacheline]);
11850Sstevel@tonic-gate 			 */
11860Sstevel@tonic-gate 			DPRINTF(MC_GUNUM_DEBUG, ("Pin number %1u\n",
11870Sstevel@tonic-gate 			    (uint_t)pinp->pintable[pos_cacheline]));
11880Sstevel@tonic-gate 			data = pinp->dimmtable[index];
11890Sstevel@tonic-gate 			idx4dimm = (data >> ((3 - offset) * 2)) & 3;
11900Sstevel@tonic-gate 
11910Sstevel@tonic-gate 			(void) strncpy(unum,
11920Sstevel@tonic-gate 			    (char *)dimmp->label[dimmoffset + idx4dimm],
11930Sstevel@tonic-gate 			    UNUM_NAMLEN);
11940Sstevel@tonic-gate 			DPRINTF(MC_GUNUM_DEBUG, ("unum %s\n", unum));
11950Sstevel@tonic-gate 			/*
11960Sstevel@tonic-gate 			 * platform hook for adding label information to unum.
11970Sstevel@tonic-gate 			 */
11980Sstevel@tonic-gate 			mc_add_mem_unum_label(unum, mcid, bankno_permc,
11990Sstevel@tonic-gate 			    idx4dimm);
12000Sstevel@tonic-gate 		} else {
12010Sstevel@tonic-gate 			char *p = unum;
12020Sstevel@tonic-gate 			size_t res = UNUM_NAMLEN;
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 			/*
12050Sstevel@tonic-gate 			 * multi-bit error handling, we can only identify
12060Sstevel@tonic-gate 			 * bank of DIMMs.
12070Sstevel@tonic-gate 			 */
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate 			for (i = 0; (i < NDIMMS) && (res > 0); i++) {
12100Sstevel@tonic-gate 				(void) snprintf(p, res, "%s%s",
12110Sstevel@tonic-gate 				    i == 0 ? "" : " ",
12120Sstevel@tonic-gate 				    (char *)dimmp->label[dimmoffset + i]);
12130Sstevel@tonic-gate 				res -= strlen(p);
12140Sstevel@tonic-gate 				p += strlen(p);
12150Sstevel@tonic-gate 			}
12160Sstevel@tonic-gate 
12170Sstevel@tonic-gate 			/*
12180Sstevel@tonic-gate 			 * platform hook for adding label information
12190Sstevel@tonic-gate 			 * to unum.
12200Sstevel@tonic-gate 			 */
12210Sstevel@tonic-gate 			mc_add_mem_unum_label(unum, mcid, bankno_permc, -1);
12220Sstevel@tonic-gate 		}
12230Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
12240Sstevel@tonic-gate 		if ((strlen(unum) >= UNUM_NAMLEN) ||
12250Sstevel@tonic-gate 		    (strlen(unum) >= buflen)) {
12266803Spothier 			return (ENAMETOOLONG);
12270Sstevel@tonic-gate 		} else {
12280Sstevel@tonic-gate 			(void) strncpy(buf, unum, buflen);
12290Sstevel@tonic-gate 			*lenp = strlen(buf);
12300Sstevel@tonic-gate 			return (0);
12310Sstevel@tonic-gate 		}
12321186Sayznaga 	}	/* end of while loop for logical bank list */
12331186Sayznaga 
12341186Sayznaga 	mutex_exit(&mcdatamutex);
12351186Sayznaga 	return (ENXIO);
12361186Sayznaga }
12371186Sayznaga 
12381186Sayznaga /* ARGSUSED */
12391186Sayznaga static int
mc_get_mem_offset(uint64_t paddr,uint64_t * offp)12401186Sayznaga mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
12411186Sayznaga {
12421186Sayznaga 	int upper_pa, lower_pa;
12431186Sayznaga 	struct bank_info *bank;
12441186Sayznaga 	struct seg_info *seg;
12451186Sayznaga 
12461186Sayznaga 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
12471186Sayznaga 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
12481186Sayznaga 
12491186Sayznaga 	/*
12501186Sayznaga 	 * Scan all logical banks to get one responding to the physical
12511186Sayznaga 	 * address.
12521186Sayznaga 	 */
12531186Sayznaga 	mutex_enter(&mcdatamutex);
12541186Sayznaga 	bank = (struct bank_info *)bank_head;
12551186Sayznaga 	while (bank != NULL) {
12561186Sayznaga 		/*
12571186Sayznaga 		 * The Address Decoding logic decodes the different fields
12581186Sayznaga 		 * in the Memory Address Decoding register to determine
12591186Sayznaga 		 * whether a particular logical bank should respond to a
12601186Sayznaga 		 * physical address.
12611186Sayznaga 		 */
12621186Sayznaga 		if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) |
12631186Sayznaga 		    bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) {
12641186Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
12651186Sayznaga 			continue;
12661186Sayznaga 		}
12671186Sayznaga 
12681186Sayznaga 		seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head);
12691186Sayznaga 		ASSERT(seg != NULL);
12701186Sayznaga 		ASSERT(paddr >= seg->base);
12711186Sayznaga 
12721186Sayznaga 		mc_addr_to_offset(seg, bank, paddr, offp);
12731186Sayznaga 
12741186Sayznaga 		mutex_exit(&mcdatamutex);
12751186Sayznaga 		return (0);
12761186Sayznaga 	}
12770Sstevel@tonic-gate 
12780Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
12790Sstevel@tonic-gate 	return (ENXIO);
12800Sstevel@tonic-gate }
12810Sstevel@tonic-gate 
12821186Sayznaga /*
12831186Sayznaga  * Translate a DIMM <id, offset> pair to a physical address.
12841186Sayznaga  */
12851186Sayznaga static int
mc_get_mem_addr(int mcid,char * sid,uint64_t off,uint64_t * paddr)12861186Sayznaga mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr)
12871186Sayznaga {
12881186Sayznaga 	struct seg_info *seg;
12891186Sayznaga 	struct bank_info *bank;
12901186Sayznaga 	int first_seg_id;
12911186Sayznaga 	int i, found;
12921186Sayznaga 
12931186Sayznaga 	ASSERT(sid != NULL);
12941186Sayznaga 
12951186Sayznaga 	mutex_enter(&mcdatamutex);
12961186Sayznaga 
12971186Sayznaga 	rw_enter(&mcdimmsids_rw, RW_READER);
12981186Sayznaga 
12991186Sayznaga 	/*
13001186Sayznaga 	 * If DIMM serial ids have not been cached yet, tell the
13011186Sayznaga 	 * caller to try again.
13021186Sayznaga 	 */
13031186Sayznaga 	if (mc_dimm_sids == NULL) {
13041186Sayznaga 		rw_exit(&mcdimmsids_rw);
13051186Sayznaga 		return (EAGAIN);
13061186Sayznaga 	}
13071186Sayznaga 
13081186Sayznaga 	for (i = 0; i < max_entries; i++) {
13091186Sayznaga 		if (mc_dimm_sids[i].mcid == mcid)
13101186Sayznaga 			break;
13111186Sayznaga 	}
13121186Sayznaga 
13131186Sayznaga 	if (i == max_entries) {
13141186Sayznaga 		rw_exit(&mcdimmsids_rw);
13151186Sayznaga 		mutex_exit(&mcdatamutex);
13161186Sayznaga 		return (ENODEV);
13171186Sayznaga 	}
13181186Sayznaga 
13191186Sayznaga 	first_seg_id = mc_dimm_sids[i].seg_id;
13201186Sayznaga 
13211186Sayznaga 	seg = (struct seg_info *)mc_node_get(first_seg_id, seg_head);
13221186Sayznaga 
13231186Sayznaga 	rw_exit(&mcdimmsids_rw);
13241186Sayznaga 
13251186Sayznaga 	if (seg == NULL) {
13261186Sayznaga 		mutex_exit(&mcdatamutex);
13271186Sayznaga 		return (ENODEV);
13281186Sayznaga 	}
13291186Sayznaga 
13301186Sayznaga 	found = 0;
13311186Sayznaga 
13321186Sayznaga 	for (bank = seg->hb_inseg; bank; bank = bank->n_inseg) {
13331186Sayznaga 		ASSERT(bank->valid);
13341186Sayznaga 
13351186Sayznaga 		for (i = 0; i < NDIMMS; i++) {
13361186Sayznaga 			if (strncmp((char *)bank->dimmsidp[i], sid,
13371186Sayznaga 			    DIMM_SERIAL_ID_LEN)  == 0)
13381186Sayznaga 				break;
13391186Sayznaga 		}
13401186Sayznaga 
13411186Sayznaga 		if (i == NDIMMS)
13421186Sayznaga 			continue;
13431186Sayznaga 
13441186Sayznaga 		if (mc_offset_to_addr(seg, bank, off, paddr) == -1)
13451186Sayznaga 			continue;
13461186Sayznaga 		found = 1;
13471186Sayznaga 		break;
13481186Sayznaga 	}
13491186Sayznaga 
13501186Sayznaga 	if (found) {
13511186Sayznaga 		mutex_exit(&mcdatamutex);
13521186Sayznaga 		return (0);
13531186Sayznaga 	}
13541186Sayznaga 
13551186Sayznaga 	/*
13561186Sayznaga 	 * If a bank wasn't found, it may be in another segment.
13571186Sayznaga 	 * This can happen if the different logical banks of an MC
13581186Sayznaga 	 * have different interleave factors.  To deal with this
13591186Sayznaga 	 * possibility, we'll do a brute-force search for banks
13601186Sayznaga 	 * for this MC with a different seg id then above.
13611186Sayznaga 	 */
13621186Sayznaga 	bank = (struct bank_info *)bank_head;
13631186Sayznaga 	while (bank != NULL) {
13641186Sayznaga 
13651186Sayznaga 		if (!bank->valid) {
13661186Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
13671186Sayznaga 			continue;
13681186Sayznaga 		}
13691186Sayznaga 
13701186Sayznaga 		if (bank->bank_node.id / NBANKS != mcid) {
13711186Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
13721186Sayznaga 			continue;
13731186Sayznaga 		}
13741186Sayznaga 
13751186Sayznaga 		/* Ignore banks in the segment we looked in above. */
13761186Sayznaga 		if (bank->seg_id == mc_dimm_sids[i].seg_id) {
13771186Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
13781186Sayznaga 			continue;
13791186Sayznaga 		}
13801186Sayznaga 
13811186Sayznaga 		for (i = 0; i < NDIMMS; i++) {
13821186Sayznaga 			if (strncmp((char *)bank->dimmsidp[i], sid,
13831186Sayznaga 			    DIMM_SERIAL_ID_LEN)  == 0)
13841186Sayznaga 				break;
13851186Sayznaga 		}
13861186Sayznaga 
13871186Sayznaga 		if (i == NDIMMS) {
13881186Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
13891186Sayznaga 			continue;
13901186Sayznaga 		}
13911186Sayznaga 
13921186Sayznaga 		seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head);
13931186Sayznaga 
13941186Sayznaga 		if (mc_offset_to_addr(seg, bank, off, paddr) == -1) {
13951186Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
13961186Sayznaga 			continue;
13971186Sayznaga 		}
13981186Sayznaga 
13991186Sayznaga 		found = 1;
14001186Sayznaga 		break;
14011186Sayznaga 	}
14021186Sayznaga 
14031186Sayznaga 	mutex_exit(&mcdatamutex);
14041186Sayznaga 
14051186Sayznaga 	if (found)
14061186Sayznaga 		return (0);
14071186Sayznaga 	else
14081186Sayznaga 		return (ENOENT);
14091186Sayznaga }
14101186Sayznaga 
14110Sstevel@tonic-gate static int
mc_get_mem_info(int synd_code,uint64_t paddr,uint64_t * mem_sizep,uint64_t * seg_sizep,uint64_t * bank_sizep,int * segsp,int * banksp,int * mcidp)14120Sstevel@tonic-gate mc_get_mem_info(int synd_code, uint64_t paddr,
14130Sstevel@tonic-gate     uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
14140Sstevel@tonic-gate     int *segsp, int *banksp, int *mcidp)
14150Sstevel@tonic-gate {
14160Sstevel@tonic-gate 	int upper_pa, lower_pa;
14170Sstevel@tonic-gate 	struct bank_info *bankp;
14180Sstevel@tonic-gate 
14190Sstevel@tonic-gate 	if (synd_code < -1 || synd_code >= QWORD_SIZE)
14200Sstevel@tonic-gate 		return (EINVAL);
14210Sstevel@tonic-gate 
14220Sstevel@tonic-gate 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
14230Sstevel@tonic-gate 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
14240Sstevel@tonic-gate 
14250Sstevel@tonic-gate 	/*
14260Sstevel@tonic-gate 	 * Scan all logical banks to get one responding to the physical
14270Sstevel@tonic-gate 	 * address.
14280Sstevel@tonic-gate 	 */
14290Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
14300Sstevel@tonic-gate 	bankp = (struct bank_info *)bank_head;
14310Sstevel@tonic-gate 	while (bankp != NULL) {
14320Sstevel@tonic-gate 		struct seg_info *segp;
14330Sstevel@tonic-gate 		int bankid, mcid;
14340Sstevel@tonic-gate 
14350Sstevel@tonic-gate 		bankid = bankp->bank_node.id;
14360Sstevel@tonic-gate 		mcid = bankid / NBANKS;
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 		/*
14390Sstevel@tonic-gate 		 * The Address Decoding logic decodes the different fields
14400Sstevel@tonic-gate 		 * in the Memory Address Decoding register to determine
14411186Sayznaga 		 * whether a particular logical bank should respond to a
14420Sstevel@tonic-gate 		 * physical address.
14430Sstevel@tonic-gate 		 */
14440Sstevel@tonic-gate 		if ((!bankp->valid) || ((~(~(upper_pa ^ bankp->um) |
14450Sstevel@tonic-gate 		    bankp->uk)) || (~(~(lower_pa ^ bankp->lm) | bankp->lk)))) {
14460Sstevel@tonic-gate 			bankp = (struct bank_info *)bankp->bank_node.next;
14470Sstevel@tonic-gate 			continue;
14480Sstevel@tonic-gate 		}
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 		/*
14510Sstevel@tonic-gate 		 * Get the corresponding segment.
14520Sstevel@tonic-gate 		 */
14530Sstevel@tonic-gate 		if ((segp = (struct seg_info *)mc_node_get(bankp->seg_id,
14540Sstevel@tonic-gate 		    seg_head)) == NULL) {
14550Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
14560Sstevel@tonic-gate 			return (EFAULT);
14570Sstevel@tonic-gate 		}
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 		*mem_sizep = memsize;
14600Sstevel@tonic-gate 		*seg_sizep = segp->size;
14610Sstevel@tonic-gate 		*bank_sizep = bankp->size;
14620Sstevel@tonic-gate 		*segsp = nsegments;
14630Sstevel@tonic-gate 		*banksp = segp->nbanks;
14640Sstevel@tonic-gate 		*mcidp = mcid;
14650Sstevel@tonic-gate 
14660Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
14670Sstevel@tonic-gate 
14680Sstevel@tonic-gate 		return (0);
14690Sstevel@tonic-gate 
14701186Sayznaga 	}	/* end of while loop for logical bank list */
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
14730Sstevel@tonic-gate 	return (ENXIO);
14740Sstevel@tonic-gate }
14750Sstevel@tonic-gate 
14760Sstevel@tonic-gate /*
14770Sstevel@tonic-gate  * Construct lists for an enabled MC where size of memory is 0.
14780Sstevel@tonic-gate  * The lists are connected as follows:
14790Sstevel@tonic-gate  * Attached MC -> device group list -> device list(per devgrp).
14800Sstevel@tonic-gate  */
14810Sstevel@tonic-gate static void
mc_construct(int mc_id,void * dimminfop)14820Sstevel@tonic-gate mc_construct(int mc_id, void *dimminfop)
14830Sstevel@tonic-gate {
14840Sstevel@tonic-gate 	int i, j, idx, dmidx;
14850Sstevel@tonic-gate 	struct mctrl_info *mctrl;
14860Sstevel@tonic-gate 	struct dgrp_info *dgrp;
14870Sstevel@tonic-gate 	struct device_info *dev;
14880Sstevel@tonic-gate 	struct	dimm_info *dimmp = (struct  dimm_info *)dimminfop;
14890Sstevel@tonic-gate 
14900Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
14910Sstevel@tonic-gate 	/* allocate for mctrl_info and bank_info */
14920Sstevel@tonic-gate 	if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id,
14930Sstevel@tonic-gate 	    mctrl_head)) != NULL) {
14940Sstevel@tonic-gate 		cmn_err(CE_WARN, "mc_construct: mctrl %d exists\n", mc_id);
14950Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
14960Sstevel@tonic-gate 		return;
14970Sstevel@tonic-gate 	}
14980Sstevel@tonic-gate 
14990Sstevel@tonic-gate 	mctrl = kmem_zalloc(sizeof (struct mctrl_info), KM_SLEEP);
15000Sstevel@tonic-gate 
15010Sstevel@tonic-gate 	/*
15020Sstevel@tonic-gate 	 * If dimminfop is NULL, the Memory Controller is disable, and
15030Sstevel@tonic-gate 	 * the number of device group will be zero.
15040Sstevel@tonic-gate 	 */
15050Sstevel@tonic-gate 	if (dimminfop == NULL) {
15060Sstevel@tonic-gate 		mctrl->mctrl_node.id = mc_id;
15070Sstevel@tonic-gate 		mctrl->ndevgrps = 0;
15080Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
15090Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
15100Sstevel@tonic-gate 		return;
15110Sstevel@tonic-gate 	}
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate 	/* add the entry on dgrp_info list */
15140Sstevel@tonic-gate 	for (i = 0; i < NDGRPS; i++) {
15150Sstevel@tonic-gate 		idx = mc_id * NDGRPS + i;
15160Sstevel@tonic-gate 		mctrl->devgrpids[i] = idx;
15170Sstevel@tonic-gate 		if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head))
15180Sstevel@tonic-gate 		    != NULL) {
15190Sstevel@tonic-gate 			cmn_err(CE_WARN, "mc_construct: devgrp %d exists\n",
15200Sstevel@tonic-gate 			    idx);
15210Sstevel@tonic-gate 			continue;
15220Sstevel@tonic-gate 		}
15230Sstevel@tonic-gate 
15240Sstevel@tonic-gate 		dgrp = kmem_zalloc(sizeof (struct dgrp_info), KM_SLEEP);
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 		/* add the entry on device_info list */
15270Sstevel@tonic-gate 		for (j = 0; j < NDIMMS; j++) {
15280Sstevel@tonic-gate 			dmidx = idx * NDIMMS + j;
15290Sstevel@tonic-gate 			dgrp->deviceids[j] = dmidx;
15300Sstevel@tonic-gate 			if ((dev = (struct device_info *)
15310Sstevel@tonic-gate 			    mc_node_get(dmidx, device_head)) != NULL) {
15320Sstevel@tonic-gate 				cmn_err(CE_WARN, "mc_construct: device %d "
15330Sstevel@tonic-gate 				    "exists\n", dmidx);
15340Sstevel@tonic-gate 				continue;
15350Sstevel@tonic-gate 			}
15360Sstevel@tonic-gate 			dev = kmem_zalloc(sizeof (struct device_info),
15370Sstevel@tonic-gate 			    KM_SLEEP);
15380Sstevel@tonic-gate 			dev->dev_node.id = dmidx;
15390Sstevel@tonic-gate 			dev->size = 0;
15400Sstevel@tonic-gate 			(void) strncpy(dev->label, (char *)
15410Sstevel@tonic-gate 			    dimmp->label[i * NDIMMS + j], MAX_DEVLEN);
15420Sstevel@tonic-gate 
15430Sstevel@tonic-gate 			mc_node_add((mc_dlist_t *)dev, &device_head,
15440Sstevel@tonic-gate 			    &device_tail);
15450Sstevel@tonic-gate 		}	/* for loop for constructing device_info */
15460Sstevel@tonic-gate 
15470Sstevel@tonic-gate 		dgrp->dgrp_node.id = idx;
15480Sstevel@tonic-gate 		dgrp->ndevices = NDIMMS;
15490Sstevel@tonic-gate 		dgrp->size = 0;
15500Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)dgrp, &dgrp_head, &dgrp_tail);
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 	}	/* end of for loop for constructing dgrp_info list */
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	mctrl->mctrl_node.id = mc_id;
15550Sstevel@tonic-gate 	mctrl->ndevgrps = NDGRPS;
15560Sstevel@tonic-gate 	mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
15570Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
15580Sstevel@tonic-gate }
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate /*
15610Sstevel@tonic-gate  * Construct lists for Memory Configuration at logical viewpoint.
15620Sstevel@tonic-gate  *
15630Sstevel@tonic-gate  * Retrieve information from Memory Address Decoding Register and set up
15640Sstevel@tonic-gate  * bank and segment lists. Link bank to its corresponding device group, and
15650Sstevel@tonic-gate  * update size of device group and devices. Also connect bank to the segment.
15660Sstevel@tonic-gate  *
15670Sstevel@tonic-gate  * Memory Address Decoding Register
15680Sstevel@tonic-gate  * -------------------------------------------------------------------------
15690Sstevel@tonic-gate  * |63|62    53|52      41|40  37|36     20|19 18|17  14|13 12|11  8|7     0|
15700Sstevel@tonic-gate  * |-----------|----------|------|---------|-----|------|-----|-----|-------|
15710Sstevel@tonic-gate  * |V |    -   |    UK    |   -  |    UM   |  -  |  LK  |  -  | LM  |   -   |
15720Sstevel@tonic-gate  * -------------------------------------------------------------------------
15730Sstevel@tonic-gate  *
15740Sstevel@tonic-gate  */
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate static int
mlayout_add(int mc_id,int bank_no,uint64_t reg,void * dimminfop)15770Sstevel@tonic-gate mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop)
15780Sstevel@tonic-gate {
15790Sstevel@tonic-gate 	int i, dmidx, idx;
15800Sstevel@tonic-gate 	uint32_t ifactor;
15810Sstevel@tonic-gate 	int status = 0;
15820Sstevel@tonic-gate 	uint64_t size, base;
15830Sstevel@tonic-gate 	struct seg_info *seg_curr;
15840Sstevel@tonic-gate 	struct bank_info *bank_curr;
15850Sstevel@tonic-gate 	struct dgrp_info *dgrp;
15860Sstevel@tonic-gate 	struct device_info *dev;
15870Sstevel@tonic-gate 	union {
15880Sstevel@tonic-gate 		struct {
15890Sstevel@tonic-gate 			uint64_t valid	: 1;
15900Sstevel@tonic-gate 			uint64_t resrv1	: 10;
15910Sstevel@tonic-gate 			uint64_t uk	: 12;
15920Sstevel@tonic-gate 			uint64_t resrv2	: 4;
15930Sstevel@tonic-gate 			uint64_t um	: 17;
15940Sstevel@tonic-gate 			uint64_t resrv3	: 2;
15950Sstevel@tonic-gate 			uint64_t lk	: 4;
15960Sstevel@tonic-gate 			uint64_t resrv4	: 2;
15970Sstevel@tonic-gate 			uint64_t lm	: 4;
15980Sstevel@tonic-gate 			uint64_t resrv5	: 8;
15990Sstevel@tonic-gate 		} _s;
16000Sstevel@tonic-gate 		uint64_t madreg;
16010Sstevel@tonic-gate 	} mcreg;
16020Sstevel@tonic-gate 
16030Sstevel@tonic-gate 	mcreg.madreg = reg;
16040Sstevel@tonic-gate 
16050Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: mc_id %d, bank num "
1606946Smathue 	    "%d, reg 0x%lx\n", mc_id, bank_no, reg));
16070Sstevel@tonic-gate 
16080Sstevel@tonic-gate 	/* add the entry on bank_info list */
16090Sstevel@tonic-gate 	idx = mc_id * NBANKS + bank_no;
16100Sstevel@tonic-gate 
16110Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
16120Sstevel@tonic-gate 	if ((bank_curr = (struct bank_info *)mc_node_get(idx, bank_head))
16130Sstevel@tonic-gate 	    != NULL) {
16140Sstevel@tonic-gate 		cmn_err(CE_WARN, "mlayout_add: bank %d exists\n", bank_no);
16150Sstevel@tonic-gate 		goto exit;
16160Sstevel@tonic-gate 	}
16170Sstevel@tonic-gate 
16180Sstevel@tonic-gate 	bank_curr = kmem_zalloc(sizeof (struct bank_info), KM_SLEEP);
16190Sstevel@tonic-gate 	bank_curr->bank_node.id = idx;
16200Sstevel@tonic-gate 	bank_curr->valid = mcreg._s.valid;
16210Sstevel@tonic-gate 	bank_curr->dimminfop = dimminfop;
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate 	if (!mcreg._s.valid) {
16240Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
16250Sstevel@tonic-gate 		goto exit;
16260Sstevel@tonic-gate 	}
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 	/*
16290Sstevel@tonic-gate 	 * size of a logical bank = size of segment / interleave factor
16300Sstevel@tonic-gate 	 * This fomula is not only working for regular configuration,
16310Sstevel@tonic-gate 	 * i.e. number of banks at a segment equals to the max
16320Sstevel@tonic-gate 	 * interleave factor, but also for special case, say 3 bank
16330Sstevel@tonic-gate 	 * interleave. One bank is 2 way interleave and other two are
16340Sstevel@tonic-gate 	 * 4 way. So the sizes of banks are size of segment/2 and /4
16350Sstevel@tonic-gate 	 * respectively.
16360Sstevel@tonic-gate 	 */
16370Sstevel@tonic-gate 	ifactor = (mcreg._s.lk ^ 0xF) + 1;
16380Sstevel@tonic-gate 	size = (((mcreg._s.uk & 0x3FF) + 1) * 0x4000000) / ifactor;
16390Sstevel@tonic-gate 	base = mcreg._s.um & ~mcreg._s.uk;
16400Sstevel@tonic-gate 	base <<= MADR_UPA_SHIFT;
16410Sstevel@tonic-gate 
16420Sstevel@tonic-gate 	bank_curr->uk = mcreg._s.uk;
16430Sstevel@tonic-gate 	bank_curr->um = mcreg._s.um;
16440Sstevel@tonic-gate 	bank_curr->lk = mcreg._s.lk;
16450Sstevel@tonic-gate 	bank_curr->lm = mcreg._s.lm;
16460Sstevel@tonic-gate 	bank_curr->size = size;
16470Sstevel@tonic-gate 
16481186Sayznaga 	/*
16491186Sayznaga 	 * The bank's position depends on which halves of the DIMMs it consists
16501186Sayznaga 	 * of. The front-side halves of the 4 DIMMs constitute the front bank
16511186Sayznaga 	 * and the back-side halves constitute the back bank. Bank numbers
16521186Sayznaga 	 * 0 and 1 are front-side banks and bank numbers 2 and 3 are back side
16531186Sayznaga 	 * banks.
16541186Sayznaga 	 */
16551186Sayznaga 	bank_curr->pos = bank_no >> 1;
16561186Sayznaga 	ASSERT((bank_curr->pos == 0) || (bank_curr->pos == 1));
16571186Sayznaga 
16587632SNick.Todd@Sun.COM 	/*
16597632SNick.Todd@Sun.COM 	 * Workaround to keep gcc and SS12 lint happy.
16607632SNick.Todd@Sun.COM 	 * Lint expects lk, uk and um in the format statement below
16617632SNick.Todd@Sun.COM 	 * to use %lx, but this produces a warning when compiled with
16627632SNick.Todd@Sun.COM 	 * gcc.
16637632SNick.Todd@Sun.COM 	 */
16647632SNick.Todd@Sun.COM 
16657632SNick.Todd@Sun.COM #if defined(lint)
16660Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add 3: logical bank num %d, "
16677632SNick.Todd@Sun.COM 	    "lk 0x%lx uk 0x%lx um 0x%lx ifactor 0x%x size 0x%lx base 0x%lx\n",
1668946Smathue 	    idx, mcreg._s.lk, mcreg._s.uk, mcreg._s.um, ifactor, size, base));
16697632SNick.Todd@Sun.COM #else /* lint */
16707632SNick.Todd@Sun.COM 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add 3: logical bank num %d, "
16717632SNick.Todd@Sun.COM 	    "lk 0x%x uk 0x%x um 0x%x ifactor 0x%x size 0x%lx base 0x%lx\n",
16727632SNick.Todd@Sun.COM 	    idx, mcreg._s.lk, mcreg._s.uk, mcreg._s.um, ifactor, size, base));
16737632SNick.Todd@Sun.COM #endif /* lint */
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate 	/* connect the entry and update the size on dgrp_info list */
16760Sstevel@tonic-gate 	idx = mc_id * NDGRPS + (bank_no % NDGRPS);
16770Sstevel@tonic-gate 	if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head)) == NULL) {
16780Sstevel@tonic-gate 		/* all avaiable dgrp should be linked at mc_construct */
16790Sstevel@tonic-gate 		cmn_err(CE_WARN, "mlayout_add: dgrp %d doesn't exist\n", idx);
16800Sstevel@tonic-gate 		kmem_free(bank_curr, sizeof (struct bank_info));
16810Sstevel@tonic-gate 		status = -1;
16820Sstevel@tonic-gate 		goto exit;
16830Sstevel@tonic-gate 	}
16840Sstevel@tonic-gate 
16850Sstevel@tonic-gate 	bank_curr->devgrp_id = idx;
16860Sstevel@tonic-gate 	dgrp->size += size;
16870Sstevel@tonic-gate 
16880Sstevel@tonic-gate 	/* Update the size of entry on device_info list */
16890Sstevel@tonic-gate 	for (i = 0; i < NDIMMS; i++) {
16900Sstevel@tonic-gate 		dmidx = dgrp->dgrp_node.id * NDIMMS + i;
16910Sstevel@tonic-gate 		dgrp->deviceids[i] = dmidx;
16920Sstevel@tonic-gate 
16930Sstevel@tonic-gate 		/* avaiable device should be linked at mc_construct */
16940Sstevel@tonic-gate 		if ((dev = (struct device_info *)mc_node_get(dmidx,
16950Sstevel@tonic-gate 		    device_head)) == NULL) {
16960Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_add:dev %d doesn't exist\n",
16970Sstevel@tonic-gate 			    dmidx);
16980Sstevel@tonic-gate 			kmem_free(bank_curr, sizeof (struct bank_info));
16990Sstevel@tonic-gate 			status = -1;
17000Sstevel@tonic-gate 			goto exit;
17010Sstevel@tonic-gate 		}
17020Sstevel@tonic-gate 
17030Sstevel@tonic-gate 		dev->size += (size / NDIMMS);
17040Sstevel@tonic-gate 
1705946Smathue 		DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add DIMM:id %d, size %lu\n",
17060Sstevel@tonic-gate 		    dmidx, size));
17070Sstevel@tonic-gate 	}
17080Sstevel@tonic-gate 
17090Sstevel@tonic-gate 	/*
17100Sstevel@tonic-gate 	 * Get the segment by matching the base address, link this bank
17110Sstevel@tonic-gate 	 * to the segment. If not matched, allocate a new segment and
17120Sstevel@tonic-gate 	 * add it at segment list.
17130Sstevel@tonic-gate 	 */
17140Sstevel@tonic-gate 	if (seg_curr = seg_match_base(base)) {
17150Sstevel@tonic-gate 		seg_curr->nbanks++;
17160Sstevel@tonic-gate 		seg_curr->size += size;
17170Sstevel@tonic-gate 		if (ifactor > seg_curr->ifactor)
17180Sstevel@tonic-gate 			seg_curr->ifactor = ifactor;
17190Sstevel@tonic-gate 		bank_curr->seg_id = seg_curr->seg_node.id;
17200Sstevel@tonic-gate 	} else {
17210Sstevel@tonic-gate 		seg_curr = (struct seg_info *)
17226803Spothier 		    kmem_zalloc(sizeof (struct seg_info), KM_SLEEP);
17230Sstevel@tonic-gate 		bank_curr->seg_id = seg_id;
17240Sstevel@tonic-gate 		seg_curr->seg_node.id = seg_id++;
17250Sstevel@tonic-gate 		seg_curr->base = base;
17260Sstevel@tonic-gate 		seg_curr->size = size;
17270Sstevel@tonic-gate 		seg_curr->nbanks = 1;
17280Sstevel@tonic-gate 		seg_curr->ifactor = ifactor;
17290Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)seg_curr, &seg_head, &seg_tail);
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate 		nsegments++;
17320Sstevel@tonic-gate 	}
17330Sstevel@tonic-gate 
17340Sstevel@tonic-gate 	/* Get the local id of bank which is only unique per segment. */
17350Sstevel@tonic-gate 	bank_curr->local_id = seg_curr->nbanks - 1;
17360Sstevel@tonic-gate 
17370Sstevel@tonic-gate 	/* add bank at the end of the list; not sorted by bankid */
17380Sstevel@tonic-gate 	if (seg_curr->hb_inseg != NULL) {
17390Sstevel@tonic-gate 		bank_curr->p_inseg = seg_curr->tb_inseg;
17400Sstevel@tonic-gate 		bank_curr->n_inseg = seg_curr->tb_inseg->n_inseg;
17410Sstevel@tonic-gate 		seg_curr->tb_inseg->n_inseg = bank_curr;
17420Sstevel@tonic-gate 		seg_curr->tb_inseg = bank_curr;
17430Sstevel@tonic-gate 	} else {
17440Sstevel@tonic-gate 		bank_curr->n_inseg = bank_curr->p_inseg = NULL;
17450Sstevel@tonic-gate 		seg_curr->hb_inseg = seg_curr->tb_inseg = bank_curr;
17460Sstevel@tonic-gate 	}
17470Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: + bank to seg, id %d\n",
17480Sstevel@tonic-gate 	    seg_curr->seg_node.id));
17490Sstevel@tonic-gate 
17501463Sayznaga 	if (mc_dimm_sids) {
17511463Sayznaga 		rw_enter(&mcdimmsids_rw, RW_WRITER);
17521463Sayznaga 		mc_update_bank(bank_curr);
17531463Sayznaga 		rw_exit(&mcdimmsids_rw);
17541463Sayznaga 	}
17550Sstevel@tonic-gate 	mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
17560Sstevel@tonic-gate 
17570Sstevel@tonic-gate 	memsize += size;
17580Sstevel@tonic-gate 	if (seg_curr->nbanks > maxbanks)
17590Sstevel@tonic-gate 		maxbanks = seg_curr->nbanks;
17600Sstevel@tonic-gate 
17610Sstevel@tonic-gate exit:
17620Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
17630Sstevel@tonic-gate 	return (status);
17640Sstevel@tonic-gate }
17650Sstevel@tonic-gate 
17660Sstevel@tonic-gate /*
17670Sstevel@tonic-gate  * Delete nodes related to the given MC on mc, device group, device,
17680Sstevel@tonic-gate  * and bank lists. Moreover, delete corresponding segment if its connected
17690Sstevel@tonic-gate  * banks are all removed.
17701186Sayznaga  *
17711186Sayznaga  * The "delete" argument is 1 if this is called as a result of DDI_DETACH. In
17721186Sayznaga  * this case, the DIMM data structures need to be deleted. The argument is
17731186Sayznaga  * 0 if this called as a result of DDI_SUSPEND/DDI_RESUME. In this case,
17741186Sayznaga  * the DIMM data structures are left alone.
17750Sstevel@tonic-gate  */
17760Sstevel@tonic-gate static void
mlayout_del(int mc_id,int delete)17771186Sayznaga mlayout_del(int mc_id, int delete)
17780Sstevel@tonic-gate {
17790Sstevel@tonic-gate 	int i, j, dgrpid, devid, bankid, ndevgrps;
17800Sstevel@tonic-gate 	struct seg_info *seg;
17810Sstevel@tonic-gate 	struct bank_info *bank_curr;
17820Sstevel@tonic-gate 	struct mctrl_info *mctrl;
17830Sstevel@tonic-gate 	mc_dlist_t *dgrp_ptr;
17840Sstevel@tonic-gate 	mc_dlist_t *dev_ptr;
17850Sstevel@tonic-gate 	uint64_t base;
17860Sstevel@tonic-gate 
17870Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
17880Sstevel@tonic-gate 
17890Sstevel@tonic-gate 	/* delete mctrl_info */
17900Sstevel@tonic-gate 	if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id, mctrl_head)) !=
17910Sstevel@tonic-gate 	    NULL) {
17920Sstevel@tonic-gate 		ndevgrps = mctrl->ndevgrps;
17930Sstevel@tonic-gate 		mc_node_del((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
17940Sstevel@tonic-gate 		kmem_free(mctrl, sizeof (struct mctrl_info));
17950Sstevel@tonic-gate 		nmcs--;
17960Sstevel@tonic-gate 
17970Sstevel@tonic-gate 		/*
17980Sstevel@tonic-gate 		 * There is no other list left for disabled MC.
17990Sstevel@tonic-gate 		 */
18000Sstevel@tonic-gate 		if (ndevgrps == 0) {
18010Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
18020Sstevel@tonic-gate 			return;
18030Sstevel@tonic-gate 		}
18040Sstevel@tonic-gate 	} else
18050Sstevel@tonic-gate 		cmn_err(CE_WARN, "MC mlayout_del: mctrl is not found\n");
18060Sstevel@tonic-gate 
18070Sstevel@tonic-gate 	/* Delete device groups and devices of the detached MC */
18080Sstevel@tonic-gate 	for (i = 0; i < NDGRPS; i++) {
18090Sstevel@tonic-gate 		dgrpid = mc_id * NDGRPS + i;
18100Sstevel@tonic-gate 		if (!(dgrp_ptr = mc_node_get(dgrpid, dgrp_head))) {
18110Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_del: no devgrp %d\n", dgrpid);
18120Sstevel@tonic-gate 			continue;
18130Sstevel@tonic-gate 		}
18140Sstevel@tonic-gate 
18150Sstevel@tonic-gate 		for (j = 0; j < NDIMMS; j++) {
18160Sstevel@tonic-gate 			devid = dgrpid * NDIMMS + j;
18170Sstevel@tonic-gate 			if (dev_ptr = mc_node_get(devid, device_head)) {
18180Sstevel@tonic-gate 				mc_node_del(dev_ptr, &device_head,
18190Sstevel@tonic-gate 				    &device_tail);
18200Sstevel@tonic-gate 				kmem_free(dev_ptr, sizeof (struct device_info));
18210Sstevel@tonic-gate 			} else {
18220Sstevel@tonic-gate 				cmn_err(CE_WARN, "mlayout_del: no dev %d\n",
18230Sstevel@tonic-gate 				    devid);
18240Sstevel@tonic-gate 			}
18250Sstevel@tonic-gate 		}
18260Sstevel@tonic-gate 
18270Sstevel@tonic-gate 		mc_node_del(dgrp_ptr, &dgrp_head, &dgrp_tail);
18280Sstevel@tonic-gate 		kmem_free(dgrp_ptr, sizeof (struct dgrp_info));
18290Sstevel@tonic-gate 	}
18300Sstevel@tonic-gate 
18310Sstevel@tonic-gate 	/* Delete banks and segments if it has no bank */
18320Sstevel@tonic-gate 	for (i = 0; i < NBANKS; i++) {
18330Sstevel@tonic-gate 		bankid = mc_id * NBANKS + i;
18340Sstevel@tonic-gate 		DPRINTF(MC_DESTRC_DEBUG, ("bank id %d\n", bankid));
18350Sstevel@tonic-gate 		if (!(bank_curr = (struct bank_info *)mc_node_get(bankid,
18360Sstevel@tonic-gate 		    bank_head))) {
18370Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_del: no bank %d\n", bankid);
18380Sstevel@tonic-gate 			continue;
18390Sstevel@tonic-gate 		}
18400Sstevel@tonic-gate 
18410Sstevel@tonic-gate 		if (bank_curr->valid) {
18420Sstevel@tonic-gate 			base = bank_curr->um & ~bank_curr->uk;
18430Sstevel@tonic-gate 			base <<= MADR_UPA_SHIFT;
18440Sstevel@tonic-gate 			bank_curr->valid = 0;
18450Sstevel@tonic-gate 			memsize -= bank_curr->size;
18460Sstevel@tonic-gate 
18470Sstevel@tonic-gate 			/* Delete bank at segment and segment if no bank left */
18480Sstevel@tonic-gate 			if (!(seg = seg_match_base(base))) {
18490Sstevel@tonic-gate 				cmn_err(CE_WARN, "mlayout_del: no seg\n");
18500Sstevel@tonic-gate 				mc_node_del((mc_dlist_t *)bank_curr, &bank_head,
18510Sstevel@tonic-gate 				    &bank_tail);
18520Sstevel@tonic-gate 				kmem_free(bank_curr, sizeof (struct bank_info));
18530Sstevel@tonic-gate 				continue;
18540Sstevel@tonic-gate 			}
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 			/* update the bank list at the segment */
18570Sstevel@tonic-gate 			if (bank_curr->n_inseg == NULL) {
18580Sstevel@tonic-gate 				/* node is at the tail of list */
18590Sstevel@tonic-gate 				seg->tb_inseg = bank_curr->p_inseg;
18600Sstevel@tonic-gate 			} else {
18610Sstevel@tonic-gate 				bank_curr->n_inseg->p_inseg =
18620Sstevel@tonic-gate 				    bank_curr->p_inseg;
18630Sstevel@tonic-gate 			}
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate 			if (bank_curr->p_inseg == NULL) {
18660Sstevel@tonic-gate 				/* node is at the head of list */
18670Sstevel@tonic-gate 				seg->hb_inseg = bank_curr->n_inseg;
18680Sstevel@tonic-gate 			} else {
18690Sstevel@tonic-gate 				bank_curr->p_inseg->n_inseg =
18700Sstevel@tonic-gate 				    bank_curr->n_inseg;
18710Sstevel@tonic-gate 			}
18720Sstevel@tonic-gate 
18730Sstevel@tonic-gate 			seg->nbanks--;
18740Sstevel@tonic-gate 			seg->size -= bank_curr->size;
18750Sstevel@tonic-gate 
18760Sstevel@tonic-gate 			if (seg->nbanks == 0) {
18770Sstevel@tonic-gate 				mc_node_del((mc_dlist_t *)seg, &seg_head,
18780Sstevel@tonic-gate 				    &seg_tail);
18790Sstevel@tonic-gate 				kmem_free(seg, sizeof (struct seg_info));
18800Sstevel@tonic-gate 				nsegments--;
18810Sstevel@tonic-gate 			}
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate 		}
18840Sstevel@tonic-gate 		mc_node_del((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
18850Sstevel@tonic-gate 		kmem_free(bank_curr, sizeof (struct bank_info));
18860Sstevel@tonic-gate 	}	/* end of for loop for four banks */
18870Sstevel@tonic-gate 
18881186Sayznaga 	if (mc_dimm_sids && delete) {
18891186Sayznaga 		rw_enter(&mcdimmsids_rw, RW_WRITER);
18901186Sayznaga 		i = mc_get_sid_cache_index(mc_id);
18911186Sayznaga 		if (i >= 0) {
18921186Sayznaga 			mc_dimm_sids[i].state = MC_DIMM_SIDS_INVALID;
18931186Sayznaga 			if (mc_dimm_sids[i].sids) {
18941186Sayznaga 				kmem_free(mc_dimm_sids[i].sids,
18951186Sayznaga 				    sizeof (dimm_sid_t) * (NDGRPS * NDIMMS));
18961186Sayznaga 				mc_dimm_sids[i].sids = NULL;
18971186Sayznaga 			}
18981186Sayznaga 		}
18991186Sayznaga 		rw_exit(&mcdimmsids_rw);
19001186Sayznaga 	}
19011186Sayznaga 
19020Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
19030Sstevel@tonic-gate }
19040Sstevel@tonic-gate 
19050Sstevel@tonic-gate /*
19060Sstevel@tonic-gate  * Search the segment in the list starting at seg_head by base address
19070Sstevel@tonic-gate  * input: base address
19080Sstevel@tonic-gate  * return: pointer of found segment or null if not found.
19090Sstevel@tonic-gate  */
19100Sstevel@tonic-gate static struct seg_info *
seg_match_base(u_longlong_t base)19110Sstevel@tonic-gate seg_match_base(u_longlong_t base)
19120Sstevel@tonic-gate {
19130Sstevel@tonic-gate 	static struct seg_info *seg_ptr;
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate 	seg_ptr = (struct seg_info *)seg_head;
19160Sstevel@tonic-gate 	while (seg_ptr != NULL) {
1917946Smathue 		DPRINTF(MC_LIST_DEBUG, ("seg_match: base %lu,given base %llu\n",
19180Sstevel@tonic-gate 		    seg_ptr->base, base));
19190Sstevel@tonic-gate 		if (seg_ptr->base == base)
19200Sstevel@tonic-gate 			break;
19210Sstevel@tonic-gate 		seg_ptr = (struct seg_info *)seg_ptr->seg_node.next;
19220Sstevel@tonic-gate 	}
19230Sstevel@tonic-gate 	return (seg_ptr);
19240Sstevel@tonic-gate }
19250Sstevel@tonic-gate 
19260Sstevel@tonic-gate /*
19270Sstevel@tonic-gate  * mc_dlist is a double linking list, including unique id, and pointers to
19280Sstevel@tonic-gate  * next, and previous nodes. seg_info, bank_info, dgrp_info, device_info,
19290Sstevel@tonic-gate  * and mctrl_info has it at the top to share the operations, add, del, and get.
19300Sstevel@tonic-gate  *
19310Sstevel@tonic-gate  * The new node is added at the tail and is not sorted.
19320Sstevel@tonic-gate  *
19330Sstevel@tonic-gate  * Input: The pointer of node to be added, head and tail of the list
19340Sstevel@tonic-gate  */
19350Sstevel@tonic-gate 
19360Sstevel@tonic-gate static void
mc_node_add(mc_dlist_t * node,mc_dlist_t ** head,mc_dlist_t ** tail)19370Sstevel@tonic-gate mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
19380Sstevel@tonic-gate {
19390Sstevel@tonic-gate 	DPRINTF(MC_LIST_DEBUG, ("mc_node_add: node->id %d head %p tail %p\n",
19407632SNick.Todd@Sun.COM 	    node->id, (void *)*head, (void *)*tail));
19410Sstevel@tonic-gate 
19420Sstevel@tonic-gate 	if (*head != NULL) {
19430Sstevel@tonic-gate 		node->prev = *tail;
19440Sstevel@tonic-gate 		node->next = (*tail)->next;
19450Sstevel@tonic-gate 		(*tail)->next = node;
19460Sstevel@tonic-gate 		*tail = node;
19470Sstevel@tonic-gate 	} else {
19480Sstevel@tonic-gate 		node->next = node->prev = NULL;
19490Sstevel@tonic-gate 		*head = *tail = node;
19500Sstevel@tonic-gate 	}
19510Sstevel@tonic-gate }
19520Sstevel@tonic-gate 
19530Sstevel@tonic-gate /*
19540Sstevel@tonic-gate  * Input: The pointer of node to be deleted, head and tail of the list
19550Sstevel@tonic-gate  *
19560Sstevel@tonic-gate  * Deleted node will be at the following positions
19570Sstevel@tonic-gate  * 1. At the tail of the list
19580Sstevel@tonic-gate  * 2. At the head of the list
19590Sstevel@tonic-gate  * 3. At the head and tail of the list, i.e. only one left.
19600Sstevel@tonic-gate  * 4. At the middle of the list
19610Sstevel@tonic-gate  */
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate static void
mc_node_del(mc_dlist_t * node,mc_dlist_t ** head,mc_dlist_t ** tail)19640Sstevel@tonic-gate mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
19650Sstevel@tonic-gate {
19660Sstevel@tonic-gate 	if (node->next == NULL) {
19670Sstevel@tonic-gate 		/* deleted node is at the tail of list */
19680Sstevel@tonic-gate 		*tail = node->prev;
19690Sstevel@tonic-gate 	} else {
19700Sstevel@tonic-gate 		node->next->prev = node->prev;
19710Sstevel@tonic-gate 	}
19720Sstevel@tonic-gate 
19730Sstevel@tonic-gate 	if (node->prev == NULL) {
19740Sstevel@tonic-gate 		/* deleted node is at the head of list */
19750Sstevel@tonic-gate 		*head = node->next;
19760Sstevel@tonic-gate 	} else {
19770Sstevel@tonic-gate 		node->prev->next = node->next;
19780Sstevel@tonic-gate 	}
19790Sstevel@tonic-gate }
19800Sstevel@tonic-gate 
19810Sstevel@tonic-gate /*
19820Sstevel@tonic-gate  * Search the list from the head of the list to match the given id
19830Sstevel@tonic-gate  * Input: id and the head of the list
19840Sstevel@tonic-gate  * Return: pointer of found node
19850Sstevel@tonic-gate  */
19860Sstevel@tonic-gate static mc_dlist_t *
mc_node_get(int id,mc_dlist_t * head)19870Sstevel@tonic-gate mc_node_get(int id, mc_dlist_t *head)
19880Sstevel@tonic-gate {
19890Sstevel@tonic-gate 	mc_dlist_t *node;
19900Sstevel@tonic-gate 
19910Sstevel@tonic-gate 	node = head;
19920Sstevel@tonic-gate 	while (node != NULL) {
19930Sstevel@tonic-gate 		DPRINTF(MC_LIST_DEBUG, ("mc_node_get: id %d, given id %d\n",
19940Sstevel@tonic-gate 		    node->id, id));
19950Sstevel@tonic-gate 		if (node->id == id)
19960Sstevel@tonic-gate 			break;
19970Sstevel@tonic-gate 		node = node->next;
19980Sstevel@tonic-gate 	}
19990Sstevel@tonic-gate 	return (node);
20000Sstevel@tonic-gate }
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate /*
20030Sstevel@tonic-gate  * mc-us3 driver allows a platform to add extra label
20040Sstevel@tonic-gate  * information to the unum string. If a platform implements a
20050Sstevel@tonic-gate  * kernel function called plat_add_mem_unum_label() it will be
20060Sstevel@tonic-gate  * executed. This would typically be implemented in the platmod.
20070Sstevel@tonic-gate  */
20080Sstevel@tonic-gate static void
mc_add_mem_unum_label(char * buf,int mcid,int bank,int dimm)20090Sstevel@tonic-gate mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm)
20100Sstevel@tonic-gate {
20110Sstevel@tonic-gate 	if (&plat_add_mem_unum_label)
20120Sstevel@tonic-gate 		plat_add_mem_unum_label(buf, mcid, bank, dimm);
20130Sstevel@tonic-gate }
20141186Sayznaga 
20151186Sayznaga static int
mc_get_sid_cache_index(int mcid)20161186Sayznaga mc_get_sid_cache_index(int mcid)
20171186Sayznaga {
20181186Sayznaga 	int	i;
20191186Sayznaga 
20201186Sayznaga 	for (i = 0; i < max_entries; i++) {
20211186Sayznaga 		if (mcid == mc_dimm_sids[i].mcid)
20221186Sayznaga 			return (i);
20231186Sayznaga 	}
20241186Sayznaga 
20251186Sayznaga 	return (-1);
20261186Sayznaga }
20271186Sayznaga 
20281463Sayznaga static void
mc_update_bank(struct bank_info * bank)20291463Sayznaga mc_update_bank(struct bank_info *bank)
20301463Sayznaga {
20311463Sayznaga 	int i, j;
20321463Sayznaga 	int bankid, mcid, dgrp_no;
20331463Sayznaga 
20341463Sayznaga 	/*
20351463Sayznaga 	 * Mark the MC if DIMM sids are not available.
20361463Sayznaga 	 * Mark which segment the DIMMs belong to.  Allocate
20371463Sayznaga 	 * space to store DIMM serial ids which are later
20381463Sayznaga 	 * provided by the platform layer, and update the bank_info
20391463Sayznaga 	 * structure with pointers to its serial ids.
20401463Sayznaga 	 */
20411463Sayznaga 	bankid = bank->bank_node.id;
20421463Sayznaga 	mcid = bankid / NBANKS;
20431463Sayznaga 	i = mc_get_sid_cache_index(mcid);
20441463Sayznaga 	if (mc_dimm_sids[i].state == MC_DIMM_SIDS_INVALID)
20451463Sayznaga 		mc_dimm_sids[i].state = MC_DIMM_SIDS_REQUESTED;
20461463Sayznaga 
20471463Sayznaga 	mc_dimm_sids[i].seg_id = bank->seg_id;
20481463Sayznaga 
20491463Sayznaga 	if (mc_dimm_sids[i].sids == NULL) {
20501463Sayznaga 		mc_dimm_sids[i].sids = (dimm_sid_t *)kmem_zalloc(
20511463Sayznaga 		    sizeof (dimm_sid_t) * (NDGRPS * NDIMMS), KM_SLEEP);
20521463Sayznaga 	}
20531463Sayznaga 
20541463Sayznaga 	dgrp_no = bank->devgrp_id % NDGRPS;
20551463Sayznaga 
20561463Sayznaga 	for (j = 0; j < NDIMMS; j++) {
20571463Sayznaga 		bank->dimmsidp[j] =
20581463Sayznaga 		    &mc_dimm_sids[i].sids[j + (NDIMMS * dgrp_no)];
20591463Sayznaga 	}
20601463Sayznaga }
20611463Sayznaga 
20621186Sayznaga static int
mc_populate_sid_cache(void)20631186Sayznaga mc_populate_sid_cache(void)
20641186Sayznaga {
20651186Sayznaga 	struct bank_info	*bank;
20661186Sayznaga 
20671186Sayznaga 	if (&plat_populate_sid_cache == 0)
20681186Sayznaga 		return (ENOTSUP);
20691186Sayznaga 
20701186Sayznaga 	ASSERT(RW_WRITE_HELD(&mcdimmsids_rw));
20711186Sayznaga 
20721186Sayznaga 	bank = (struct bank_info *)bank_head;
20731186Sayznaga 	while (bank != NULL) {
20741186Sayznaga 		if (!bank->valid) {
20751186Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
20761186Sayznaga 			continue;
20771186Sayznaga 		}
20781186Sayznaga 
20791463Sayznaga 		mc_update_bank(bank);
20801186Sayznaga 
20811186Sayznaga 		bank = (struct bank_info *)bank->bank_node.next;
20821186Sayznaga 	}
20831186Sayznaga 
20841186Sayznaga 
20851186Sayznaga 	/*
20861186Sayznaga 	 * Call to the platform layer to populate the cache
20871186Sayznaga 	 * with DIMM serial ids.
20881186Sayznaga 	 */
20891186Sayznaga 	return (plat_populate_sid_cache(mc_dimm_sids, max_entries));
20901186Sayznaga }
20911186Sayznaga 
20921186Sayznaga static void
mc_init_sid_cache_thr(void)20931186Sayznaga mc_init_sid_cache_thr(void)
20941186Sayznaga {
20951186Sayznaga 	ASSERT(mc_dimm_sids == NULL);
20961186Sayznaga 
20971186Sayznaga 	mutex_enter(&mcdatamutex);
20981186Sayznaga 	rw_enter(&mcdimmsids_rw, RW_WRITER);
20991186Sayznaga 
21001186Sayznaga 	mc_dimm_sids = plat_alloc_sid_cache(&max_entries);
21011186Sayznaga 	(void) mc_populate_sid_cache();
21021186Sayznaga 
21031186Sayznaga 	rw_exit(&mcdimmsids_rw);
21041186Sayznaga 	mutex_exit(&mcdatamutex);
21051186Sayznaga }
21061186Sayznaga 
21071186Sayznaga static int
mc_init_sid_cache(void)21081186Sayznaga mc_init_sid_cache(void)
21091186Sayznaga {
21101186Sayznaga 	if (&plat_alloc_sid_cache) {
21111186Sayznaga 		(void) thread_create(NULL, 0, mc_init_sid_cache_thr, NULL, 0,
21121186Sayznaga 		    &p0, TS_RUN, minclsyspri);
21131186Sayznaga 		return (0);
21141186Sayznaga 	} else
21151186Sayznaga 		return (ENOTSUP);
21161186Sayznaga }
21171186Sayznaga 
21181186Sayznaga static int
mc_get_mem_sid(int mcid,int dimm,char * buf,int buflen,int * lenp)21191186Sayznaga mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp)
21201186Sayznaga {
21211186Sayznaga 	int	i;
21221186Sayznaga 
21231186Sayznaga 	if (buflen < DIMM_SERIAL_ID_LEN)
21241186Sayznaga 		return (ENOSPC);
21251186Sayznaga 
21261186Sayznaga 	/*
21271186Sayznaga 	 * If DIMM serial ids have not been cached yet, tell the
21281186Sayznaga 	 * caller to try again.
21291186Sayznaga 	 */
21301186Sayznaga 	if (!rw_tryenter(&mcdimmsids_rw, RW_READER))
21311186Sayznaga 		return (EAGAIN);
21321186Sayznaga 
21331186Sayznaga 	if (mc_dimm_sids == NULL) {
21341186Sayznaga 		rw_exit(&mcdimmsids_rw);
21351186Sayznaga 		return (EAGAIN);
21361186Sayznaga 	}
21371186Sayznaga 
21381186Sayznaga 	/*
21391186Sayznaga 	 * Find dimm serial id using mcid and dimm #
21401186Sayznaga 	 */
21411186Sayznaga 	for (i = 0; i < max_entries; i++) {
21421186Sayznaga 		if (mc_dimm_sids[i].mcid == mcid)
21431186Sayznaga 			break;
21441186Sayznaga 	}
21451186Sayznaga 	if ((i == max_entries) || (!mc_dimm_sids[i].sids)) {
21461186Sayznaga 		rw_exit(&mcdimmsids_rw);
21471186Sayznaga 		return (ENOENT);
21481186Sayznaga 	}
21491186Sayznaga 
21501186Sayznaga 	(void) strlcpy(buf, mc_dimm_sids[i].sids[dimm],
21511186Sayznaga 	    DIMM_SERIAL_ID_LEN);
21521186Sayznaga 	*lenp = strlen(buf);
21531186Sayznaga 
21541186Sayznaga 	rw_exit(&mcdimmsids_rw);
21551186Sayznaga 	return (0);
21561186Sayznaga }
2157