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