xref: /illumos-gate/usr/src/uts/sun4u/io/mc-us3.c (revision d00f0155)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23f47a9c50Smathue  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/conf.h>
317c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
327c478bd9Sstevel@tonic-gate #include <sys/stat.h>
337c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
347c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
357c478bd9Sstevel@tonic-gate #include <sys/obpdefs.h>
367c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
377c478bd9Sstevel@tonic-gate #include <sys/errno.h>
387c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
397c478bd9Sstevel@tonic-gate #include <sys/open.h>
407c478bd9Sstevel@tonic-gate #include <sys/thread.h>
417c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
427c478bd9Sstevel@tonic-gate #include <sys/x_call.h>
437c478bd9Sstevel@tonic-gate #include <sys/debug.h>
447c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
457c478bd9Sstevel@tonic-gate #include <sys/ivintr.h>
467c478bd9Sstevel@tonic-gate #include <sys/intr.h>
477c478bd9Sstevel@tonic-gate #include <sys/intreg.h>
487c478bd9Sstevel@tonic-gate #include <sys/autoconf.h>
497c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
507c478bd9Sstevel@tonic-gate #include <sys/spl.h>
517c478bd9Sstevel@tonic-gate #include <sys/async.h>
527c478bd9Sstevel@tonic-gate #include <sys/mc.h>
537c478bd9Sstevel@tonic-gate #include <sys/mc-us3.h>
547c478bd9Sstevel@tonic-gate #include <sys/cpu_module.h>
55*d00f0155Sayznaga #include <sys/platform_module.h>
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * Function prototypes
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate static int mc_open(dev_t *, int, int, cred_t *);
627c478bd9Sstevel@tonic-gate static int mc_close(dev_t, int, int, cred_t *);
637c478bd9Sstevel@tonic-gate static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
647c478bd9Sstevel@tonic-gate static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
657c478bd9Sstevel@tonic-gate static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate /*
687c478bd9Sstevel@tonic-gate  * Configuration data structures
697c478bd9Sstevel@tonic-gate  */
707c478bd9Sstevel@tonic-gate static struct cb_ops mc_cb_ops = {
717c478bd9Sstevel@tonic-gate 	mc_open,			/* open */
727c478bd9Sstevel@tonic-gate 	mc_close,			/* close */
737c478bd9Sstevel@tonic-gate 	nulldev,			/* strategy */
747c478bd9Sstevel@tonic-gate 	nulldev,			/* print */
757c478bd9Sstevel@tonic-gate 	nodev,				/* dump */
767c478bd9Sstevel@tonic-gate 	nulldev,			/* read */
777c478bd9Sstevel@tonic-gate 	nulldev,			/* write */
787c478bd9Sstevel@tonic-gate 	mc_ioctl,			/* ioctl */
797c478bd9Sstevel@tonic-gate 	nodev,				/* devmap */
807c478bd9Sstevel@tonic-gate 	nodev,				/* mmap */
817c478bd9Sstevel@tonic-gate 	nodev,				/* segmap */
827c478bd9Sstevel@tonic-gate 	nochpoll,			/* poll */
837c478bd9Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
847c478bd9Sstevel@tonic-gate 	0,				/* streamtab */
857c478bd9Sstevel@tonic-gate 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
867c478bd9Sstevel@tonic-gate 	CB_REV,				/* rev */
877c478bd9Sstevel@tonic-gate 	nodev,				/* cb_aread */
887c478bd9Sstevel@tonic-gate 	nodev				/* cb_awrite */
897c478bd9Sstevel@tonic-gate };
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate static struct dev_ops mc_ops = {
927c478bd9Sstevel@tonic-gate 	DEVO_REV,			/* rev */
937c478bd9Sstevel@tonic-gate 	0,				/* refcnt  */
947c478bd9Sstevel@tonic-gate 	ddi_getinfo_1to1,		/* getinfo */
957c478bd9Sstevel@tonic-gate 	nulldev,			/* identify */
967c478bd9Sstevel@tonic-gate 	nulldev,			/* probe */
977c478bd9Sstevel@tonic-gate 	mc_attach,			/* attach */
987c478bd9Sstevel@tonic-gate 	mc_detach,			/* detach */
997c478bd9Sstevel@tonic-gate 	nulldev,			/* reset */
1007c478bd9Sstevel@tonic-gate 	&mc_cb_ops,			/* cb_ops */
1017c478bd9Sstevel@tonic-gate 	(struct bus_ops *)0,		/* bus_ops */
1027c478bd9Sstevel@tonic-gate 	nulldev				/* power */
1037c478bd9Sstevel@tonic-gate };
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * Driver globals
1077c478bd9Sstevel@tonic-gate  */
1087c478bd9Sstevel@tonic-gate static void *mcp;
1097c478bd9Sstevel@tonic-gate static int nmcs = 0;
1107c478bd9Sstevel@tonic-gate static int seg_id = 0;
1117c478bd9Sstevel@tonic-gate static int nsegments = 0;
1127c478bd9Sstevel@tonic-gate static uint64_t memsize = 0;
1137c478bd9Sstevel@tonic-gate static int maxbanks = 0;
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate static mc_dlist_t *seg_head, *seg_tail, *bank_head, *bank_tail;
1167c478bd9Sstevel@tonic-gate static mc_dlist_t *mctrl_head, *mctrl_tail, *dgrp_head, *dgrp_tail;
1177c478bd9Sstevel@tonic-gate static mc_dlist_t *device_head, *device_tail;
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate static kmutex_t	mcmutex;
1207c478bd9Sstevel@tonic-gate static kmutex_t	mcdatamutex;
1217c478bd9Sstevel@tonic-gate static int mc_is_open = 0;
1227c478bd9Sstevel@tonic-gate 
123*d00f0155Sayznaga static krwlock_t mcdimmsids_rw;
124*d00f0155Sayznaga 
125*d00f0155Sayznaga /* pointer to cache of DIMM serial ids */
126*d00f0155Sayznaga static dimm_sid_cache_t	*mc_dimm_sids;
127*d00f0155Sayznaga static int		max_entries;
128*d00f0155Sayznaga 
1297c478bd9Sstevel@tonic-gate extern struct mod_ops mod_driverops;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
1327c478bd9Sstevel@tonic-gate 	&mod_driverops,			/* module type, this one is a driver */
1337c478bd9Sstevel@tonic-gate 	"Memory-controller: %I%",	/* module name */
1347c478bd9Sstevel@tonic-gate 	&mc_ops,			/* driver ops */
1357c478bd9Sstevel@tonic-gate };
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1387c478bd9Sstevel@tonic-gate 	MODREV_1,		/* rev */
1397c478bd9Sstevel@tonic-gate 	(void *)&modldrv,
1407c478bd9Sstevel@tonic-gate 	NULL
1417c478bd9Sstevel@tonic-gate };
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate static int mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf,
1447c478bd9Sstevel@tonic-gate     int buflen, int *lenp);
1457c478bd9Sstevel@tonic-gate static int mc_get_mem_info(int synd_code, uint64_t paddr,
1467c478bd9Sstevel@tonic-gate     uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
1477c478bd9Sstevel@tonic-gate     int *segsp, int *banksp, int *mcidp);
148*d00f0155Sayznaga static int mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp);
149*d00f0155Sayznaga static int mc_get_mem_offset(uint64_t paddr, uint64_t *offp);
150*d00f0155Sayznaga static int mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr);
151*d00f0155Sayznaga static int mc_init_sid_cache(void);
1527c478bd9Sstevel@tonic-gate static int mc_get_mcregs(struct mc_soft_state *);
1537c478bd9Sstevel@tonic-gate static void mc_construct(int mc_id, void *dimminfop);
1547c478bd9Sstevel@tonic-gate static int mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop);
155*d00f0155Sayznaga static void mlayout_del(int mc_id, int delete);
1567c478bd9Sstevel@tonic-gate static struct seg_info *seg_match_base(u_longlong_t base);
1577c478bd9Sstevel@tonic-gate static void mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
1587c478bd9Sstevel@tonic-gate static void mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
1597c478bd9Sstevel@tonic-gate static mc_dlist_t *mc_node_get(int id, mc_dlist_t *head);
1607c478bd9Sstevel@tonic-gate static void mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm);
161*d00f0155Sayznaga static int mc_populate_sid_cache(void);
162*d00f0155Sayznaga static int mc_get_sid_cache_index(int mcid);
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate #pragma weak p2get_mem_unum
1657c478bd9Sstevel@tonic-gate #pragma weak p2get_mem_info
166*d00f0155Sayznaga #pragma weak p2get_mem_sid
167*d00f0155Sayznaga #pragma weak p2get_mem_offset
168*d00f0155Sayznaga #pragma	weak p2get_mem_addr
169*d00f0155Sayznaga #pragma weak p2init_sid_cache
1707c478bd9Sstevel@tonic-gate #pragma weak plat_add_mem_unum_label
171*d00f0155Sayznaga #pragma weak plat_alloc_sid_cache
172*d00f0155Sayznaga #pragma weak plat_populate_sid_cache
173*d00f0155Sayznaga 
174*d00f0155Sayznaga #define	QWORD_SIZE		144
175*d00f0155Sayznaga #define	QWORD_SIZE_BYTES	(QWORD_SIZE / 8)
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate /*
1787c478bd9Sstevel@tonic-gate  * These are the module initialization routines.
1797c478bd9Sstevel@tonic-gate  */
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate int
1827c478bd9Sstevel@tonic-gate _init(void)
1837c478bd9Sstevel@tonic-gate {
1847c478bd9Sstevel@tonic-gate 	int error;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	if ((error = ddi_soft_state_init(&mcp,
1877c478bd9Sstevel@tonic-gate 	    sizeof (struct mc_soft_state), 1)) != 0)
1887c478bd9Sstevel@tonic-gate 		return (error);
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	error =  mod_install(&modlinkage);
1917c478bd9Sstevel@tonic-gate 	if (error == 0) {
1927c478bd9Sstevel@tonic-gate 		mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
1937c478bd9Sstevel@tonic-gate 		mutex_init(&mcdatamutex, NULL, MUTEX_DRIVER, NULL);
194*d00f0155Sayznaga 		rw_init(&mcdimmsids_rw, NULL, RW_DRIVER, NULL);
1957c478bd9Sstevel@tonic-gate 	}
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 	return (error);
1987c478bd9Sstevel@tonic-gate }
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate int
2017c478bd9Sstevel@tonic-gate _fini(void)
2027c478bd9Sstevel@tonic-gate {
2037c478bd9Sstevel@tonic-gate 	int error;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	if ((error = mod_remove(&modlinkage)) != 0)
2067c478bd9Sstevel@tonic-gate 		return (error);
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	ddi_soft_state_fini(&mcp);
2097c478bd9Sstevel@tonic-gate 	mutex_destroy(&mcmutex);
2107c478bd9Sstevel@tonic-gate 	mutex_destroy(&mcdatamutex);
211*d00f0155Sayznaga 	rw_destroy(&mcdimmsids_rw);
212*d00f0155Sayznaga 
213*d00f0155Sayznaga 	if (mc_dimm_sids)
214*d00f0155Sayznaga 		kmem_free(mc_dimm_sids, sizeof (dimm_sid_cache_t) *
215*d00f0155Sayznaga 		    max_entries);
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	return (0);
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate int
2217c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2227c478bd9Sstevel@tonic-gate {
2237c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate static int
2277c478bd9Sstevel@tonic-gate mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	struct mc_soft_state *softsp;
2307c478bd9Sstevel@tonic-gate 	struct dimm_info *dimminfop;
2317c478bd9Sstevel@tonic-gate 	int instance, len, err;
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	/* get the instance of this devi */
2347c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(devi);
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	switch (cmd) {
2377c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
2387c478bd9Sstevel@tonic-gate 		break;
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
2417c478bd9Sstevel@tonic-gate 		/* get the soft state pointer for this device node */
2427c478bd9Sstevel@tonic-gate 		softsp = ddi_get_soft_state(mcp, instance);
2437c478bd9Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: DDI_RESUME: updating MADRs\n",
2447c478bd9Sstevel@tonic-gate 		    instance));
2457c478bd9Sstevel@tonic-gate 		/*
2467c478bd9Sstevel@tonic-gate 		 * During resume, the source and target board's bank_infos
2477c478bd9Sstevel@tonic-gate 		 * need to be updated with the new mc MADR values.  This is
2487c478bd9Sstevel@tonic-gate 		 * implemented with existing functionality by first removing
2497c478bd9Sstevel@tonic-gate 		 * the props and allocated data structs, and then adding them
2507c478bd9Sstevel@tonic-gate 		 * back in.
2517c478bd9Sstevel@tonic-gate 		 */
2527c478bd9Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
2537c478bd9Sstevel@tonic-gate 		    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
2547c478bd9Sstevel@tonic-gate 		    MEM_CFG_PROP_NAME) == 1) {
2557c478bd9Sstevel@tonic-gate 			(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
2567c478bd9Sstevel@tonic-gate 			    MEM_CFG_PROP_NAME);
2577c478bd9Sstevel@tonic-gate 		}
258*d00f0155Sayznaga 		mlayout_del(softsp->portid, 0);
2597c478bd9Sstevel@tonic-gate 		if (mc_get_mcregs(softsp) == -1) {
2607c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "mc_attach: mc%d DDI_RESUME failure\n",
2617c478bd9Sstevel@tonic-gate 			    instance);
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	default:
2667c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2677c478bd9Sstevel@tonic-gate 	}
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(mcp, instance) != DDI_SUCCESS)
2707c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	softsp = ddi_get_soft_state(mcp, instance);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	/* Set the dip in the soft state */
2757c478bd9Sstevel@tonic-gate 	softsp->dip = devi;
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	if ((softsp->portid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
2787c478bd9Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
2797c478bd9Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to get %s property",
2807c478bd9Sstevel@tonic-gate 		    instance, "portid"));
2817c478bd9Sstevel@tonic-gate 		goto bad;
2827c478bd9Sstevel@tonic-gate 	}
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	DPRINTF(MC_ATTACH_DEBUG, ("mc%d ATTACH: portid %d, cpuid %d\n",
2857c478bd9Sstevel@tonic-gate 	    instance, softsp->portid, CPU->cpu_id));
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	/* map in the registers for this device. */
2887c478bd9Sstevel@tonic-gate 	if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0)) {
2897c478bd9Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to map registers",
2907c478bd9Sstevel@tonic-gate 		    instance));
2917c478bd9Sstevel@tonic-gate 		goto bad;
2927c478bd9Sstevel@tonic-gate 	}
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	/*
2957c478bd9Sstevel@tonic-gate 	 * Get the label of dimms and pin routing information at memory-layout
2967c478bd9Sstevel@tonic-gate 	 * property if the memory controller is enabled.
2977c478bd9Sstevel@tonic-gate 	 *
2987c478bd9Sstevel@tonic-gate 	 * Basically every memory-controller node on every machine should
2997c478bd9Sstevel@tonic-gate 	 * have one of these properties unless the memory controller is
3007c478bd9Sstevel@tonic-gate 	 * physically not capable of having memory attached to it, e.g.
3017c478bd9Sstevel@tonic-gate 	 * Excalibur's slave processor.
3027c478bd9Sstevel@tonic-gate 	 */
3037c478bd9Sstevel@tonic-gate 	err = ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip, DDI_PROP_DONTPASS,
3047c478bd9Sstevel@tonic-gate 	    "memory-layout", (caddr_t)&dimminfop, &len);
3057c478bd9Sstevel@tonic-gate 	if (err == DDI_PROP_SUCCESS) {
3067c478bd9Sstevel@tonic-gate 		/*
3077c478bd9Sstevel@tonic-gate 		 * Set the pointer and size of property in the soft state
3087c478bd9Sstevel@tonic-gate 		 */
3097c478bd9Sstevel@tonic-gate 		softsp->memlayoutp = dimminfop;
3107c478bd9Sstevel@tonic-gate 		softsp->size = len;
3117c478bd9Sstevel@tonic-gate 	} else if (err == DDI_PROP_NOT_FOUND) {
3127c478bd9Sstevel@tonic-gate 		/*
3137c478bd9Sstevel@tonic-gate 		 * This is a disable MC. Clear out the pointer and size
3147c478bd9Sstevel@tonic-gate 		 * of property in the soft state
3157c478bd9Sstevel@tonic-gate 		 */
3167c478bd9Sstevel@tonic-gate 		softsp->memlayoutp = NULL;
3177c478bd9Sstevel@tonic-gate 		softsp->size = 0;
3187c478bd9Sstevel@tonic-gate 	} else {
3197c478bd9Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d is disabled: dimminfop %p\n",
3207c478bd9Sstevel@tonic-gate 		    instance, dimminfop));
3217c478bd9Sstevel@tonic-gate 		goto bad2;
3227c478bd9Sstevel@tonic-gate 	}
3237c478bd9Sstevel@tonic-gate 
324f47a9c50Smathue 	DPRINTF(MC_ATTACH_DEBUG, ("mc%d: dimminfop=0x%p data=0x%lx len=%d\n",
325f47a9c50Smathue 	    instance, dimminfop, *(uint64_t *)dimminfop, len));
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	/* Get MC registers and construct all needed data structure */
3287c478bd9Sstevel@tonic-gate 	if (mc_get_mcregs(softsp) == -1)
3297c478bd9Sstevel@tonic-gate 		goto bad1;
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	mutex_enter(&mcmutex);
3327c478bd9Sstevel@tonic-gate 	if (nmcs == 1) {
3337c478bd9Sstevel@tonic-gate 		if (&p2get_mem_unum)
3347c478bd9Sstevel@tonic-gate 			p2get_mem_unum = mc_get_mem_unum;
3357c478bd9Sstevel@tonic-gate 		if (&p2get_mem_info)
3367c478bd9Sstevel@tonic-gate 			p2get_mem_info = mc_get_mem_info;
337*d00f0155Sayznaga 		if (&p2get_mem_sid)
338*d00f0155Sayznaga 			p2get_mem_sid = mc_get_mem_sid;
339*d00f0155Sayznaga 		if (&p2get_mem_offset)
340*d00f0155Sayznaga 			p2get_mem_offset = mc_get_mem_offset;
341*d00f0155Sayznaga 		if (&p2get_mem_addr)
342*d00f0155Sayznaga 			p2get_mem_addr = mc_get_mem_addr;
343*d00f0155Sayznaga 		if (&p2init_sid_cache)
344*d00f0155Sayznaga 			p2init_sid_cache = mc_init_sid_cache;
3457c478bd9Sstevel@tonic-gate 	}
346*d00f0155Sayznaga 
3477c478bd9Sstevel@tonic-gate 	mutex_exit(&mcmutex);
3487c478bd9Sstevel@tonic-gate 
349*d00f0155Sayznaga 	/*
350*d00f0155Sayznaga 	 * Update DIMM serial id information if the DIMM serial id
351*d00f0155Sayznaga 	 * cache has already been initialized.
352*d00f0155Sayznaga 	 */
353*d00f0155Sayznaga 	if (mc_dimm_sids) {
354*d00f0155Sayznaga 		rw_enter(&mcdimmsids_rw, RW_WRITER);
355*d00f0155Sayznaga 		(void) mc_populate_sid_cache();
356*d00f0155Sayznaga 		rw_exit(&mcdimmsids_rw);
357*d00f0155Sayznaga 	}
358*d00f0155Sayznaga 
3597c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "mc-us3", S_IFCHR, instance,
3607c478bd9Sstevel@tonic-gate 	    "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
3617c478bd9Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc_attach: create_minor_node"
3627c478bd9Sstevel@tonic-gate 		    " failed \n"));
3637c478bd9Sstevel@tonic-gate 		goto bad1;
3647c478bd9Sstevel@tonic-gate 	}
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	ddi_report_dev(devi);
3677c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate bad1:
3707c478bd9Sstevel@tonic-gate 	/* release all allocated data struture for this MC */
371*d00f0155Sayznaga 	mlayout_del(softsp->portid, 0);
3727c478bd9Sstevel@tonic-gate 	if (softsp->memlayoutp != NULL)
3737c478bd9Sstevel@tonic-gate 		kmem_free(softsp->memlayoutp, softsp->size);
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate 	/* remove the libdevinfo property */
3767c478bd9Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
3777c478bd9Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
3787c478bd9Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) == 1) {
3797c478bd9Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
3807c478bd9Sstevel@tonic-gate 			MEM_CFG_PROP_NAME);
3817c478bd9Sstevel@tonic-gate 	}
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate bad2:
3847c478bd9Sstevel@tonic-gate 	/* unmap the registers for this device. */
3857c478bd9Sstevel@tonic-gate 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate bad:
3887c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(mcp, instance);
3897c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
3907c478bd9Sstevel@tonic-gate }
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate /* ARGSUSED */
3937c478bd9Sstevel@tonic-gate static int
3947c478bd9Sstevel@tonic-gate mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3957c478bd9Sstevel@tonic-gate {
3967c478bd9Sstevel@tonic-gate 	int instance;
3977c478bd9Sstevel@tonic-gate 	struct mc_soft_state *softsp;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	/* get the instance of this devi */
4007c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(devi);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	/* get the soft state pointer for this device node */
4037c478bd9Sstevel@tonic-gate 	softsp = ddi_get_soft_state(mcp, instance);
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	switch (cmd) {
4067c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
4077c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
4107c478bd9Sstevel@tonic-gate 		break;
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	default:
4137c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
4147c478bd9Sstevel@tonic-gate 	}
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	DPRINTF(MC_DETACH_DEBUG, ("mc%d DETACH: portid= %d, table 0x%p\n",
4177c478bd9Sstevel@tonic-gate 	    instance, softsp->portid, softsp->memlayoutp));
4187c478bd9Sstevel@tonic-gate 
4197c478bd9Sstevel@tonic-gate 	/* remove the libdevinfo property */
4207c478bd9Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
4217c478bd9Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
4227c478bd9Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) == 1) {
4237c478bd9Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
4247c478bd9Sstevel@tonic-gate 			MEM_CFG_PROP_NAME);
4257c478bd9Sstevel@tonic-gate 	}
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	/* release all allocated data struture for this MC */
428*d00f0155Sayznaga 	mlayout_del(softsp->portid, 1);
4297c478bd9Sstevel@tonic-gate 	if (softsp->memlayoutp != NULL)
4307c478bd9Sstevel@tonic-gate 		kmem_free(softsp->memlayoutp, softsp->size);
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	/* unmap the registers */
4337c478bd9Sstevel@tonic-gate 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	mutex_enter(&mcmutex);
4367c478bd9Sstevel@tonic-gate 	if (nmcs == 0) {
4377c478bd9Sstevel@tonic-gate 		if (&p2get_mem_unum)
4387c478bd9Sstevel@tonic-gate 			p2get_mem_unum = NULL;
4397c478bd9Sstevel@tonic-gate 		if (&p2get_mem_info)
4407c478bd9Sstevel@tonic-gate 			p2get_mem_info = NULL;
441*d00f0155Sayznaga 		if (&p2get_mem_sid)
442*d00f0155Sayznaga 			p2get_mem_sid = NULL;
443*d00f0155Sayznaga 		if (&p2get_mem_offset)
444*d00f0155Sayznaga 			p2get_mem_offset = NULL;
445*d00f0155Sayznaga 		if (&p2get_mem_addr)
446*d00f0155Sayznaga 			p2get_mem_addr = NULL;
447*d00f0155Sayznaga 		if (&p2init_sid_cache)
448*d00f0155Sayznaga 			p2init_sid_cache = NULL;
4497c478bd9Sstevel@tonic-gate 	}
450*d00f0155Sayznaga 
4517c478bd9Sstevel@tonic-gate 	mutex_exit(&mcmutex);
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	/* free up the soft state */
4567c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(mcp, instance);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4597c478bd9Sstevel@tonic-gate }
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate /* ARGSUSED */
4627c478bd9Sstevel@tonic-gate static int
4637c478bd9Sstevel@tonic-gate mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4647c478bd9Sstevel@tonic-gate {
4657c478bd9Sstevel@tonic-gate 	int status = 0;
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate 	/* verify that otyp is appropriate */
4687c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR) {
4697c478bd9Sstevel@tonic-gate 		return (EINVAL);
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	mutex_enter(&mcmutex);
4737c478bd9Sstevel@tonic-gate 	if (mc_is_open) {
4747c478bd9Sstevel@tonic-gate 		status = EBUSY;
4757c478bd9Sstevel@tonic-gate 		goto bad;
4767c478bd9Sstevel@tonic-gate 	}
4777c478bd9Sstevel@tonic-gate 	mc_is_open = 1;
4787c478bd9Sstevel@tonic-gate bad:
4797c478bd9Sstevel@tonic-gate 	mutex_exit(&mcmutex);
4807c478bd9Sstevel@tonic-gate 	return (status);
4817c478bd9Sstevel@tonic-gate }
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate /* ARGSUSED */
4847c478bd9Sstevel@tonic-gate static int
4857c478bd9Sstevel@tonic-gate mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
4867c478bd9Sstevel@tonic-gate {
4877c478bd9Sstevel@tonic-gate 	mutex_enter(&mcmutex);
4887c478bd9Sstevel@tonic-gate 	mc_is_open = 0;
4897c478bd9Sstevel@tonic-gate 	mutex_exit(&mcmutex);
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	return (0);
4927c478bd9Sstevel@tonic-gate }
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate /*
4957c478bd9Sstevel@tonic-gate  * cmd includes MCIOC_MEMCONF, MCIOC_MEM, MCIOC_SEG, MCIOC_BANK, MCIOC_DEVGRP,
4967c478bd9Sstevel@tonic-gate  * MCIOC_CTRLCONF, MCIOC_CONTROL.
4977c478bd9Sstevel@tonic-gate  *
4987c478bd9Sstevel@tonic-gate  * MCIOC_MEM, MCIOC_SEG, MCIOC_CTRLCONF, and MCIOC_CONTROL are
4997c478bd9Sstevel@tonic-gate  * associated with various length struct. If given number is less than the
5007c478bd9Sstevel@tonic-gate  * number in kernel, update the number and return EINVAL so that user could
5017c478bd9Sstevel@tonic-gate  * allocate enough space for it.
5027c478bd9Sstevel@tonic-gate  *
5037c478bd9Sstevel@tonic-gate  */
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate /* ARGSUSED */
5067c478bd9Sstevel@tonic-gate static int
5077c478bd9Sstevel@tonic-gate mc_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
5087c478bd9Sstevel@tonic-gate 	int *rval_p)
5097c478bd9Sstevel@tonic-gate {
5107c478bd9Sstevel@tonic-gate 	size_t	size;
5117c478bd9Sstevel@tonic-gate 	struct mc_memconf mcmconf;
5127c478bd9Sstevel@tonic-gate 	struct mc_memory *mcmem, mcmem_in;
5137c478bd9Sstevel@tonic-gate 	struct mc_segment *mcseg, mcseg_in;
5147c478bd9Sstevel@tonic-gate 	struct mc_bank mcbank;
5157c478bd9Sstevel@tonic-gate 	struct mc_devgrp mcdevgrp;
5167c478bd9Sstevel@tonic-gate 	struct mc_ctrlconf *mcctrlconf, mcctrlconf_in;
5177c478bd9Sstevel@tonic-gate 	struct mc_control *mccontrol, mccontrol_in;
5187c478bd9Sstevel@tonic-gate 	struct seg_info *seg = NULL;
5197c478bd9Sstevel@tonic-gate 	struct bank_info *bank = NULL;
5207c478bd9Sstevel@tonic-gate 	struct dgrp_info *dgrp = NULL;
5217c478bd9Sstevel@tonic-gate 	struct mctrl_info *mcport;
5227c478bd9Sstevel@tonic-gate 	mc_dlist_t *mctrl;
5237c478bd9Sstevel@tonic-gate 	int i, status = 0;
5247c478bd9Sstevel@tonic-gate 	cpu_t *cpu;
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 	switch (cmd) {
5277c478bd9Sstevel@tonic-gate 	case MCIOC_MEMCONF:
5287c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 		mcmconf.nmcs = nmcs;
5317c478bd9Sstevel@tonic-gate 		mcmconf.nsegments = nsegments;
5327c478bd9Sstevel@tonic-gate 		mcmconf.nbanks = maxbanks;
5337c478bd9Sstevel@tonic-gate 		mcmconf.ndevgrps = NDGRPS;
5347c478bd9Sstevel@tonic-gate 		mcmconf.ndevs = NDIMMS;
5357c478bd9Sstevel@tonic-gate 		mcmconf.len_dev = MAX_DEVLEN;
5367c478bd9Sstevel@tonic-gate 		mcmconf.xfer_size = TRANSFER_SIZE;
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 		if (copyout(&mcmconf, (void *)arg, sizeof (struct mc_memconf)))
5417c478bd9Sstevel@tonic-gate 			return (EFAULT);
5427c478bd9Sstevel@tonic-gate 		return (0);
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/*
5457c478bd9Sstevel@tonic-gate 	 * input: nsegments and allocate space for various length of segmentids
5467c478bd9Sstevel@tonic-gate 	 *
5477c478bd9Sstevel@tonic-gate 	 * return    0: size, number of segments, and all segment ids,
5487c478bd9Sstevel@tonic-gate 	 *		where glocal and local ids are identical.
5497c478bd9Sstevel@tonic-gate 	 *	EINVAL: if the given nsegments is less than that in kernel and
5507c478bd9Sstevel@tonic-gate 	 *		nsegments of struct will be updated.
5517c478bd9Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
5527c478bd9Sstevel@tonic-gate 	 */
5537c478bd9Sstevel@tonic-gate 	case MCIOC_MEM:
5547c478bd9Sstevel@tonic-gate 		if (copyin((void *)arg, &mcmem_in,
5557c478bd9Sstevel@tonic-gate 		    sizeof (struct mc_memory)) != 0)
5567c478bd9Sstevel@tonic-gate 			return (EFAULT);
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
5597c478bd9Sstevel@tonic-gate 		if (mcmem_in.nsegments < nsegments) {
5607c478bd9Sstevel@tonic-gate 			mcmem_in.nsegments = nsegments;
5617c478bd9Sstevel@tonic-gate 			if (copyout(&mcmem_in, (void *)arg,
5627c478bd9Sstevel@tonic-gate 			    sizeof (struct mc_memory)))
5637c478bd9Sstevel@tonic-gate 				status = EFAULT;
5647c478bd9Sstevel@tonic-gate 			else
5657c478bd9Sstevel@tonic-gate 				status = EINVAL;
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
5687c478bd9Sstevel@tonic-gate 			return (status);
5697c478bd9Sstevel@tonic-gate 		}
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 		size = sizeof (struct mc_memory) + (nsegments - 1) *
5727c478bd9Sstevel@tonic-gate 		    sizeof (mcmem->segmentids[0]);
5737c478bd9Sstevel@tonic-gate 		mcmem = kmem_zalloc(size, KM_SLEEP);
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 		mcmem->size = memsize;
5767c478bd9Sstevel@tonic-gate 		mcmem->nsegments = nsegments;
5777c478bd9Sstevel@tonic-gate 		seg = (struct seg_info *)seg_head;
5787c478bd9Sstevel@tonic-gate 		for (i = 0; i < nsegments; i++) {
5797c478bd9Sstevel@tonic-gate 			ASSERT(seg != NULL);
5807c478bd9Sstevel@tonic-gate 			mcmem->segmentids[i].globalid = seg->seg_node.id;
5817c478bd9Sstevel@tonic-gate 			mcmem->segmentids[i].localid = seg->seg_node.id;
5827c478bd9Sstevel@tonic-gate 			seg = (struct seg_info *)seg->seg_node.next;
5837c478bd9Sstevel@tonic-gate 		}
5847c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 		if (copyout(mcmem, (void *)arg, size))
5877c478bd9Sstevel@tonic-gate 			status = EFAULT;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 		kmem_free(mcmem, size);
5907c478bd9Sstevel@tonic-gate 		return (status);
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	/*
5937c478bd9Sstevel@tonic-gate 	 * input: id, nbanks and allocate space for various length of bankids
5947c478bd9Sstevel@tonic-gate 	 *
5957c478bd9Sstevel@tonic-gate 	 * return    0: base, size, number of banks, and all bank ids,
5967c478bd9Sstevel@tonic-gate 	 *		where global id is unique of all banks and local id
5977c478bd9Sstevel@tonic-gate 	 *		is only unique for mc.
5987c478bd9Sstevel@tonic-gate 	 *	EINVAL: either id isn't found or if given nbanks is less than
5997c478bd9Sstevel@tonic-gate 	 *		that in kernel and nbanks of struct will be updated.
6007c478bd9Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
6017c478bd9Sstevel@tonic-gate 	 */
6027c478bd9Sstevel@tonic-gate 	case MCIOC_SEG:
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 		if (copyin((void *)arg, &mcseg_in,
6057c478bd9Sstevel@tonic-gate 		    sizeof (struct mc_segment)) != 0)
6067c478bd9Sstevel@tonic-gate 			return (EFAULT);
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
6097c478bd9Sstevel@tonic-gate 		if ((seg = (struct seg_info *)mc_node_get(mcseg_in.id,
6107c478bd9Sstevel@tonic-gate 		    seg_head)) == NULL) {
6117c478bd9Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG: seg not match, "
6127c478bd9Sstevel@tonic-gate 			    "id %d\n", mcseg_in.id));
6137c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
6147c478bd9Sstevel@tonic-gate 			return (EFAULT);
6157c478bd9Sstevel@tonic-gate 		}
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 		if (mcseg_in.nbanks < seg->nbanks) {
6187c478bd9Sstevel@tonic-gate 			mcseg_in.nbanks = seg->nbanks;
6197c478bd9Sstevel@tonic-gate 			if (copyout(&mcseg_in, (void *)arg,
6207c478bd9Sstevel@tonic-gate 			    sizeof (struct mc_segment)))
6217c478bd9Sstevel@tonic-gate 				status = EFAULT;
6227c478bd9Sstevel@tonic-gate 			else
6237c478bd9Sstevel@tonic-gate 				status = EINVAL;
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
6267c478bd9Sstevel@tonic-gate 			return (status);
6277c478bd9Sstevel@tonic-gate 		}
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 		size = sizeof (struct mc_segment) + (seg->nbanks - 1) *
6307c478bd9Sstevel@tonic-gate 		    sizeof (mcseg->bankids[0]);
6317c478bd9Sstevel@tonic-gate 		mcseg = kmem_zalloc(size, KM_SLEEP);
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 		mcseg->id = seg->seg_node.id;
6347c478bd9Sstevel@tonic-gate 		mcseg->ifactor = seg->ifactor;
6357c478bd9Sstevel@tonic-gate 		mcseg->base = seg->base;
6367c478bd9Sstevel@tonic-gate 		mcseg->size = seg->size;
6377c478bd9Sstevel@tonic-gate 		mcseg->nbanks = seg->nbanks;
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 		bank = seg->hb_inseg;
6407c478bd9Sstevel@tonic-gate 
6417c478bd9Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:nbanks %d seg 0x%p bank %p\n",
6427c478bd9Sstevel@tonic-gate 		    seg->nbanks, seg, bank));
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 		i = 0;
6457c478bd9Sstevel@tonic-gate 		while (bank != NULL) {
6467c478bd9Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:idx %d bank_id %d\n",
6477c478bd9Sstevel@tonic-gate 			    i, bank->bank_node.id));
6487c478bd9Sstevel@tonic-gate 			mcseg->bankids[i].globalid = bank->bank_node.id;
6497c478bd9Sstevel@tonic-gate 			mcseg->bankids[i++].localid =
6507c478bd9Sstevel@tonic-gate 			    bank->local_id;
6517c478bd9Sstevel@tonic-gate 			bank = bank->n_inseg;
6527c478bd9Sstevel@tonic-gate 		}
6537c478bd9Sstevel@tonic-gate 		ASSERT(i == seg->nbanks);
6547c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 		if (copyout(mcseg, (void *)arg, size))
6577c478bd9Sstevel@tonic-gate 			status = EFAULT;
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 		kmem_free(mcseg, size);
6607c478bd9Sstevel@tonic-gate 		return (status);
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	/*
6637c478bd9Sstevel@tonic-gate 	 * input: id
6647c478bd9Sstevel@tonic-gate 	 *
6657c478bd9Sstevel@tonic-gate 	 * return    0: mask, match, size, and devgrpid,
6667c478bd9Sstevel@tonic-gate 	 *		where global id is unique of all devgrps and local id
6677c478bd9Sstevel@tonic-gate 	 *		is only unique for mc.
6687c478bd9Sstevel@tonic-gate 	 *	EINVAL: if id isn't found
6697c478bd9Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
6707c478bd9Sstevel@tonic-gate 	 */
6717c478bd9Sstevel@tonic-gate 	case MCIOC_BANK:
6727c478bd9Sstevel@tonic-gate 		if (copyin((void *)arg, &mcbank, sizeof (struct mc_bank)) != 0)
6737c478bd9Sstevel@tonic-gate 			return (EFAULT);
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank id %d\n", mcbank.id));
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 		if ((bank = (struct bank_info *)mc_node_get(mcbank.id,
6807c478bd9Sstevel@tonic-gate 		    bank_head)) == NULL) {
6817c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
6827c478bd9Sstevel@tonic-gate 			return (EINVAL);
6837c478bd9Sstevel@tonic-gate 		}
6847c478bd9Sstevel@tonic-gate 
685f47a9c50Smathue 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank %d (0x%p) valid %hu\n",
6867c478bd9Sstevel@tonic-gate 		    bank->bank_node.id, bank, bank->valid));
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 		/*
6897c478bd9Sstevel@tonic-gate 		 * If (Physic Address & MASK) == MATCH, Physic Address is
6907c478bd9Sstevel@tonic-gate 		 * located at this bank. The lower physical address bits
6917c478bd9Sstevel@tonic-gate 		 * are at [9-6].
6927c478bd9Sstevel@tonic-gate 		 */
6937c478bd9Sstevel@tonic-gate 		mcbank.mask = (~(bank->lk | ~(MADR_LK_MASK >>
6947c478bd9Sstevel@tonic-gate 		    MADR_LK_SHIFT))) << MADR_LPA_SHIFT;
6957c478bd9Sstevel@tonic-gate 		mcbank.match = bank->lm << MADR_LPA_SHIFT;
6967c478bd9Sstevel@tonic-gate 		mcbank.size = bank->size;
6977c478bd9Sstevel@tonic-gate 		mcbank.devgrpid.globalid = bank->devgrp_id;
6987c478bd9Sstevel@tonic-gate 		mcbank.devgrpid.localid = bank->devgrp_id % NDGRPS;
6997c478bd9Sstevel@tonic-gate 
7007c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate 		if (copyout(&mcbank, (void *)arg, sizeof (struct mc_bank)))
7037c478bd9Sstevel@tonic-gate 			return (EFAULT);
7047c478bd9Sstevel@tonic-gate 		return (0);
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	/*
7077c478bd9Sstevel@tonic-gate 	 * input:id and allocate space for various length of deviceids
7087c478bd9Sstevel@tonic-gate 	 *
7097c478bd9Sstevel@tonic-gate 	 * return    0: size and number of devices.
7107c478bd9Sstevel@tonic-gate 	 *	EINVAL: id isn't found
7117c478bd9Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
7127c478bd9Sstevel@tonic-gate 	 */
7137c478bd9Sstevel@tonic-gate 	case MCIOC_DEVGRP:
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 		if (copyin((void *)arg, &mcdevgrp,
7167c478bd9Sstevel@tonic-gate 		    sizeof (struct mc_devgrp)) != 0)
7177c478bd9Sstevel@tonic-gate 			return (EFAULT);
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
7207c478bd9Sstevel@tonic-gate 		if ((dgrp = (struct dgrp_info *)mc_node_get(mcdevgrp.id,
7217c478bd9Sstevel@tonic-gate 		    dgrp_head)) == NULL) {
7227c478bd9Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_DEVGRP: not match, id "
7237c478bd9Sstevel@tonic-gate 			    "%d\n", mcdevgrp.id));
7247c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
7257c478bd9Sstevel@tonic-gate 			return (EINVAL);
7267c478bd9Sstevel@tonic-gate 		}
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 		mcdevgrp.ndevices = dgrp->ndevices;
7297c478bd9Sstevel@tonic-gate 		mcdevgrp.size = dgrp->size;
7307c478bd9Sstevel@tonic-gate 
7317c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 		if (copyout(&mcdevgrp, (void *)arg, sizeof (struct mc_devgrp)))
7347c478bd9Sstevel@tonic-gate 			status = EFAULT;
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate 		return (status);
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate 	/*
7397c478bd9Sstevel@tonic-gate 	 * input: nmcs and allocate space for various length of mcids
7407c478bd9Sstevel@tonic-gate 	 *
7417c478bd9Sstevel@tonic-gate 	 * return    0: number of mc, and all mcids,
7427c478bd9Sstevel@tonic-gate 	 *		where glocal and local ids are identical.
7437c478bd9Sstevel@tonic-gate 	 *	EINVAL: if the given nmcs is less than that in kernel and
7447c478bd9Sstevel@tonic-gate 	 *		nmcs of struct will be updated.
7457c478bd9Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
7467c478bd9Sstevel@tonic-gate 	 */
7477c478bd9Sstevel@tonic-gate 	case MCIOC_CTRLCONF:
7487c478bd9Sstevel@tonic-gate 		if (copyin((void *)arg, &mcctrlconf_in,
7497c478bd9Sstevel@tonic-gate 		    sizeof (struct mc_ctrlconf)) != 0)
7507c478bd9Sstevel@tonic-gate 			return (EFAULT);
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
7537c478bd9Sstevel@tonic-gate 		if (mcctrlconf_in.nmcs < nmcs) {
7547c478bd9Sstevel@tonic-gate 			mcctrlconf_in.nmcs = nmcs;
7557c478bd9Sstevel@tonic-gate 			if (copyout(&mcctrlconf_in, (void *)arg,
7567c478bd9Sstevel@tonic-gate 			    sizeof (struct mc_ctrlconf)))
7577c478bd9Sstevel@tonic-gate 				status = EFAULT;
7587c478bd9Sstevel@tonic-gate 			else
7597c478bd9Sstevel@tonic-gate 				status = EINVAL;
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
7627c478bd9Sstevel@tonic-gate 			return (status);
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 
7657c478bd9Sstevel@tonic-gate 		/*
7667c478bd9Sstevel@tonic-gate 		 * Cannot just use the size of the struct because of the various
7677c478bd9Sstevel@tonic-gate 		 * length struct
7687c478bd9Sstevel@tonic-gate 		 */
7697c478bd9Sstevel@tonic-gate 		size = sizeof (struct mc_ctrlconf) + ((nmcs - 1) *
7707c478bd9Sstevel@tonic-gate 		    sizeof (mcctrlconf->mcids[0]));
7717c478bd9Sstevel@tonic-gate 		mcctrlconf = kmem_zalloc(size, KM_SLEEP);
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate 		mcctrlconf->nmcs = nmcs;
7747c478bd9Sstevel@tonic-gate 
7757c478bd9Sstevel@tonic-gate 		/* Get all MC ids and add to mcctrlconf */
7767c478bd9Sstevel@tonic-gate 		mctrl = mctrl_head;
7777c478bd9Sstevel@tonic-gate 		i = 0;
7787c478bd9Sstevel@tonic-gate 		while (mctrl != NULL) {
7797c478bd9Sstevel@tonic-gate 			mcctrlconf->mcids[i].globalid = mctrl->id;
7807c478bd9Sstevel@tonic-gate 			mcctrlconf->mcids[i].localid = mctrl->id;
7817c478bd9Sstevel@tonic-gate 			i++;
7827c478bd9Sstevel@tonic-gate 			mctrl = mctrl->next;
7837c478bd9Sstevel@tonic-gate 		}
7847c478bd9Sstevel@tonic-gate 		ASSERT(i == nmcs);
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 		if (copyout(mcctrlconf, (void *)arg, size))
7897c478bd9Sstevel@tonic-gate 			status = EFAULT;
7907c478bd9Sstevel@tonic-gate 
7917c478bd9Sstevel@tonic-gate 		kmem_free(mcctrlconf, size);
7927c478bd9Sstevel@tonic-gate 		return (status);
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	/*
7957c478bd9Sstevel@tonic-gate 	 * input:id, ndevgrps and allocate space for various length of devgrpids
7967c478bd9Sstevel@tonic-gate 	 *
7977c478bd9Sstevel@tonic-gate 	 * return    0: number of devgrp, and all devgrpids,
7987c478bd9Sstevel@tonic-gate 	 *		is unique of all devgrps and local id is only unique
7997c478bd9Sstevel@tonic-gate 	 *		for mc.
8007c478bd9Sstevel@tonic-gate 	 *	EINVAL: either if id isn't found or if the given ndevgrps is
8017c478bd9Sstevel@tonic-gate 	 *		less than that in kernel and ndevgrps of struct will
8027c478bd9Sstevel@tonic-gate 	 *		be updated.
8037c478bd9Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
8047c478bd9Sstevel@tonic-gate 	 */
8057c478bd9Sstevel@tonic-gate 	case MCIOC_CONTROL:
8067c478bd9Sstevel@tonic-gate 		if (copyin((void *)arg, &mccontrol_in,
8077c478bd9Sstevel@tonic-gate 		    sizeof (struct mc_control)) != 0)
8087c478bd9Sstevel@tonic-gate 			return (EFAULT);
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
8117c478bd9Sstevel@tonic-gate 		if ((mcport = (struct mctrl_info *)mc_node_get(mccontrol_in.id,
8127c478bd9Sstevel@tonic-gate 		    mctrl_head)) == NULL) {
8137c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
8147c478bd9Sstevel@tonic-gate 			return (EINVAL);
8157c478bd9Sstevel@tonic-gate 		}
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 		/*
8187c478bd9Sstevel@tonic-gate 		 * mcport->ndevgrps zero means Memory Controller is disable.
8197c478bd9Sstevel@tonic-gate 		 */
8207c478bd9Sstevel@tonic-gate 		if ((mccontrol_in.ndevgrps < mcport->ndevgrps) ||
8217c478bd9Sstevel@tonic-gate 		    (mcport->ndevgrps == 0)) {
8227c478bd9Sstevel@tonic-gate 			mccontrol_in.ndevgrps = mcport->ndevgrps;
8237c478bd9Sstevel@tonic-gate 			if (copyout(&mccontrol_in, (void *)arg,
8247c478bd9Sstevel@tonic-gate 			    sizeof (struct mc_control)))
8257c478bd9Sstevel@tonic-gate 				status = EFAULT;
8267c478bd9Sstevel@tonic-gate 			else if (mcport->ndevgrps != 0)
8277c478bd9Sstevel@tonic-gate 				status = EINVAL;
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
8307c478bd9Sstevel@tonic-gate 			return (status);
8317c478bd9Sstevel@tonic-gate 		}
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 		size = sizeof (struct mc_control) + (mcport->ndevgrps - 1) *
8347c478bd9Sstevel@tonic-gate 		    sizeof (mccontrol->devgrpids[0]);
8357c478bd9Sstevel@tonic-gate 		mccontrol = kmem_zalloc(size, KM_SLEEP);
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 		mccontrol->id = mcport->mctrl_node.id;
8387c478bd9Sstevel@tonic-gate 		mccontrol->ndevgrps = mcport->ndevgrps;
8397c478bd9Sstevel@tonic-gate 		for (i = 0; i < mcport->ndevgrps; i++) {
8407c478bd9Sstevel@tonic-gate 			mccontrol->devgrpids[i].globalid = mcport->devgrpids[i];
8417c478bd9Sstevel@tonic-gate 			mccontrol->devgrpids[i].localid =
8427c478bd9Sstevel@tonic-gate 			    mcport->devgrpids[i] % NDGRPS;
843f47a9c50Smathue 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_CONTROL: devgrp id %lu\n",
844f47a9c50Smathue 			    *(uint64_t *)&mccontrol->devgrpids[i]));
8457c478bd9Sstevel@tonic-gate 		}
8467c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 		if (copyout(mccontrol, (void *)arg, size))
8497c478bd9Sstevel@tonic-gate 			status = EFAULT;
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 		kmem_free(mccontrol, size);
8527c478bd9Sstevel@tonic-gate 		return (status);
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate 	/*
8557c478bd9Sstevel@tonic-gate 	 * input:id
8567c478bd9Sstevel@tonic-gate 	 *
8577c478bd9Sstevel@tonic-gate 	 * return    0: CPU flushed successfully.
8587c478bd9Sstevel@tonic-gate 	 *	EINVAL: the id wasn't found
8597c478bd9Sstevel@tonic-gate 	 */
8607c478bd9Sstevel@tonic-gate 	case MCIOC_ECFLUSH:
8617c478bd9Sstevel@tonic-gate 		mutex_enter(&cpu_lock);
8627c478bd9Sstevel@tonic-gate 		cpu = cpu_get((processorid_t)arg);
8637c478bd9Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
8647c478bd9Sstevel@tonic-gate 		if (cpu == NULL)
8657c478bd9Sstevel@tonic-gate 			return (EINVAL);
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate 		xc_one(arg, (xcfunc_t *)cpu_flush_ecache, 0, 0);
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 		return (0);
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	default:
8727c478bd9Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("DEFAULT: cmd is wrong\n"));
8737c478bd9Sstevel@tonic-gate 		return (EFAULT);
8747c478bd9Sstevel@tonic-gate 	}
8757c478bd9Sstevel@tonic-gate }
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate /*
8787c478bd9Sstevel@tonic-gate  * Get Memory Address Decoding Registers and construct list.
8797c478bd9Sstevel@tonic-gate  * flag is to workaround Cheetah's restriction where register cannot be mapped
8807c478bd9Sstevel@tonic-gate  * if port id(MC registers on it) == cpu id(process is running on it).
8817c478bd9Sstevel@tonic-gate  */
8827c478bd9Sstevel@tonic-gate static int
8837c478bd9Sstevel@tonic-gate mc_get_mcregs(struct mc_soft_state *softsp)
8847c478bd9Sstevel@tonic-gate {
8857c478bd9Sstevel@tonic-gate 	int i;
8867c478bd9Sstevel@tonic-gate 	int err = 0;
8877c478bd9Sstevel@tonic-gate 	uint64_t madreg;
8887c478bd9Sstevel@tonic-gate 	uint64_t ma_reg_array[NBANKS];	/* there are NBANKS of madrs */
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	/* Construct lists for MC, mctrl_info, dgrp_info, and device_info */
8917c478bd9Sstevel@tonic-gate 	mc_construct(softsp->portid, softsp->memlayoutp);
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	/*
8947c478bd9Sstevel@tonic-gate 	 * If memlayoutp is NULL, the Memory Controller is disable, and
8957c478bd9Sstevel@tonic-gate 	 * doesn't need to create any bank and segment.
8967c478bd9Sstevel@tonic-gate 	 */
8977c478bd9Sstevel@tonic-gate 	if (softsp->memlayoutp == NULL)
8987c478bd9Sstevel@tonic-gate 		goto exit;
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	/*
9017c478bd9Sstevel@tonic-gate 	 * Get the content of 4 Memory Address Decoding Registers, and
9027c478bd9Sstevel@tonic-gate 	 * construct lists of logical banks and segments.
9037c478bd9Sstevel@tonic-gate 	 */
9047c478bd9Sstevel@tonic-gate 	for (i = 0; i < NBANKS; i++) {
9057c478bd9Sstevel@tonic-gate 		DPRINTF(MC_REG_DEBUG, ("get_mcregs: mapreg=0x%p portid=%d "
9067c478bd9Sstevel@tonic-gate 		    "cpu=%d\n", softsp->mc_base, softsp->portid, CPU->cpu_id));
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 		kpreempt_disable();
9097c478bd9Sstevel@tonic-gate 		if (softsp->portid == (cpunodes[CPU->cpu_id].portid))
9107c478bd9Sstevel@tonic-gate 			madreg = get_mcr(MADR0OFFSET + (i * REGOFFSET));
9117c478bd9Sstevel@tonic-gate 		else
9127c478bd9Sstevel@tonic-gate 			madreg = *((uint64_t *)(softsp->mc_base + MADR0OFFSET +
9137c478bd9Sstevel@tonic-gate 			    (i * REGOFFSET)));
9147c478bd9Sstevel@tonic-gate 		kpreempt_enable();
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 		DPRINTF(MC_REG_DEBUG, ("get_mcregs 2: memlayoutp=0x%p madreg "
917f47a9c50Smathue 		    "reg=0x%lx\n", softsp->memlayoutp, madreg));
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 		ma_reg_array[i] = madreg;
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 		if ((err = mlayout_add(softsp->portid, i, madreg,
9227c478bd9Sstevel@tonic-gate 		    softsp->memlayoutp)) == -1)
9237c478bd9Sstevel@tonic-gate 			break;
9247c478bd9Sstevel@tonic-gate 	}
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 	/*
9277c478bd9Sstevel@tonic-gate 	 * Create the logical bank property for this mc node. This
9287c478bd9Sstevel@tonic-gate 	 * property is an encoded array of the madr for each logical
9297c478bd9Sstevel@tonic-gate 	 * bank (there are NBANKS of these).
9307c478bd9Sstevel@tonic-gate 	 */
9317c478bd9Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
9327c478bd9Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
9337c478bd9Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) != 1) {
9347c478bd9Sstevel@tonic-gate 		(void) ddi_prop_create(DDI_DEV_T_NONE, softsp->dip,
9357c478bd9Sstevel@tonic-gate 			DDI_PROP_CANSLEEP, MEM_CFG_PROP_NAME,
9367c478bd9Sstevel@tonic-gate 			(caddr_t)&ma_reg_array, sizeof (ma_reg_array));
9377c478bd9Sstevel@tonic-gate 	}
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate exit:
9407c478bd9Sstevel@tonic-gate 	if (!err) {
9417c478bd9Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
9427c478bd9Sstevel@tonic-gate 		nmcs++;
9437c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
9447c478bd9Sstevel@tonic-gate 	}
9457c478bd9Sstevel@tonic-gate 	return (err);
9467c478bd9Sstevel@tonic-gate }
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate /*
949*d00f0155Sayznaga  * Translate a <DIMM, offset> pair to a physical address.
950*d00f0155Sayznaga  */
951*d00f0155Sayznaga static int
952*d00f0155Sayznaga mc_offset_to_addr(struct seg_info *seg,
953*d00f0155Sayznaga     struct bank_info *bank, uint64_t off, uint64_t *addr)
954*d00f0155Sayznaga {
955*d00f0155Sayznaga 	uint64_t base, size, line, remainder;
956*d00f0155Sayznaga 	uint32_t ifactor;
957*d00f0155Sayznaga 
958*d00f0155Sayznaga 	/*
959*d00f0155Sayznaga 	 * Compute the half-dimm size in bytes.
960*d00f0155Sayznaga 	 * Note that bank->size represents the number of data bytes,
961*d00f0155Sayznaga 	 * and does not include the additional bits used for ecc, mtag,
962*d00f0155Sayznaga 	 * and mtag ecc information in each 144-bit checkword.
963*d00f0155Sayznaga 	 * For calculating the offset to a checkword we need the size
964*d00f0155Sayznaga 	 * including the additional 8 bytes for each 64 data bytes of
965*d00f0155Sayznaga 	 * a cache line.
966*d00f0155Sayznaga 	 */
967*d00f0155Sayznaga 	size = ((bank->size / 4) / 64) * 72;
968*d00f0155Sayznaga 
969*d00f0155Sayznaga 	/*
970*d00f0155Sayznaga 	 * Check if the offset is within this bank. This depends on the position
971*d00f0155Sayznaga 	 * of the bank, i.e., whether it is the front bank or the back bank.
972*d00f0155Sayznaga 	 */
973*d00f0155Sayznaga 	base = size * bank->pos;
974*d00f0155Sayznaga 
975*d00f0155Sayznaga 	if ((off < base) || (off >= (base + size)))
976*d00f0155Sayznaga 		return (-1);
977*d00f0155Sayznaga 
978*d00f0155Sayznaga 	/*
979*d00f0155Sayznaga 	 * Compute the offset within the half-dimm.
980*d00f0155Sayznaga 	 */
981*d00f0155Sayznaga 	off -= base;
982*d00f0155Sayznaga 
983*d00f0155Sayznaga 	/*
984*d00f0155Sayznaga 	 * Compute the line within the half-dimm. This is the same as the line
985*d00f0155Sayznaga 	 * within the bank since each DIMM in a bank contributes uniformly
986*d00f0155Sayznaga 	 * 144 bits (18 bytes) to a cache line.
987*d00f0155Sayznaga 	 */
988*d00f0155Sayznaga 	line = off / QWORD_SIZE_BYTES;
989*d00f0155Sayznaga 
990*d00f0155Sayznaga 	remainder = off % QWORD_SIZE_BYTES;
991*d00f0155Sayznaga 
992*d00f0155Sayznaga 	/*
993*d00f0155Sayznaga 	 * Compute the line within the segment.
994*d00f0155Sayznaga 	 * The bank->lm field indicates the order in which cache lines are
995*d00f0155Sayznaga 	 * distributed across the banks of a segment (See the Cheetah PRM).
996*d00f0155Sayznaga 	 * The interleave factor the bank is programmed with is used instead
997*d00f0155Sayznaga 	 * of the segment interleave factor since a segment can be composed
998*d00f0155Sayznaga 	 * of banks with different interleave factors if the banks are not
999*d00f0155Sayznaga 	 * uniform in size.
1000*d00f0155Sayznaga 	 */
1001*d00f0155Sayznaga 	ifactor = (bank->lk ^ 0xF) + 1;
1002*d00f0155Sayznaga 	line = (line * ifactor) + bank->lm;
1003*d00f0155Sayznaga 
1004*d00f0155Sayznaga 	/*
1005*d00f0155Sayznaga 	 * Compute the physical address assuming that there are 64 data bytes
1006*d00f0155Sayznaga 	 * in a cache line.
1007*d00f0155Sayznaga 	 */
1008*d00f0155Sayznaga 	*addr = (line << 6) + seg->base;
1009*d00f0155Sayznaga 	*addr += remainder * 16;
1010*d00f0155Sayznaga 
1011*d00f0155Sayznaga 	return (0);
1012*d00f0155Sayznaga }
1013*d00f0155Sayznaga 
1014*d00f0155Sayznaga /*
1015*d00f0155Sayznaga  * Translate a physical address to a <DIMM, offset> pair.
1016*d00f0155Sayznaga  */
1017*d00f0155Sayznaga static void
1018*d00f0155Sayznaga mc_addr_to_offset(struct seg_info *seg,
1019*d00f0155Sayznaga     struct bank_info *bank, uint64_t addr, uint64_t *off)
1020*d00f0155Sayznaga {
1021*d00f0155Sayznaga 	uint64_t base, size, line, remainder;
1022*d00f0155Sayznaga 	uint32_t ifactor;
1023*d00f0155Sayznaga 
1024*d00f0155Sayznaga 	/*
1025*d00f0155Sayznaga 	 * Compute the line within the segment assuming that there are 64 data
1026*d00f0155Sayznaga 	 * bytes in a cache line.
1027*d00f0155Sayznaga 	 */
1028*d00f0155Sayznaga 	line = (addr - seg->base) / 64;
1029*d00f0155Sayznaga 
1030*d00f0155Sayznaga 	/*
1031*d00f0155Sayznaga 	 * The lm (lower match) field from the Memory Address Decoding Register
1032*d00f0155Sayznaga 	 * for this bank determines which lines within a memory segment this
1033*d00f0155Sayznaga 	 * bank should respond to.  These are the actual address bits the
1034*d00f0155Sayznaga 	 * interleave is done over (See the Cheetah PRM).
1035*d00f0155Sayznaga 	 * In other words, the lm field indicates the order in which the cache
1036*d00f0155Sayznaga 	 * lines are distributed across the banks of a segment, and thusly it
1037*d00f0155Sayznaga 	 * can be used to compute the line within this bank. This is the same as
1038*d00f0155Sayznaga 	 * the line within the half-dimm. This is because each DIMM in a bank
1039*d00f0155Sayznaga 	 * contributes uniformly to every cache line.
1040*d00f0155Sayznaga 	 */
1041*d00f0155Sayznaga 	ifactor = (bank->lk ^ 0xF) + 1;
1042*d00f0155Sayznaga 	line = (line - bank->lm)/ifactor;
1043*d00f0155Sayznaga 
1044*d00f0155Sayznaga 	/*
1045*d00f0155Sayznaga 	 * Compute the offset within the half-dimm. This depends on whether
1046*d00f0155Sayznaga 	 * or not the bank is a front logical bank or a back logical bank.
1047*d00f0155Sayznaga 	 */
1048*d00f0155Sayznaga 	*off = line * QWORD_SIZE_BYTES;
1049*d00f0155Sayznaga 
1050*d00f0155Sayznaga 	/*
1051*d00f0155Sayznaga 	 * Compute the half-dimm size in bytes.
1052*d00f0155Sayznaga 	 * Note that bank->size represents the number of data bytes,
1053*d00f0155Sayznaga 	 * and does not include the additional bits used for ecc, mtag,
1054*d00f0155Sayznaga 	 * and mtag ecc information in each 144-bit quadword.
1055*d00f0155Sayznaga 	 * For calculating the offset to a checkword we need the size
1056*d00f0155Sayznaga 	 * including the additional 8 bytes for each 64 data bytes of
1057*d00f0155Sayznaga 	 * a cache line.
1058*d00f0155Sayznaga 	 */
1059*d00f0155Sayznaga 	size = ((bank->size / 4) / 64) * 72;
1060*d00f0155Sayznaga 
1061*d00f0155Sayznaga 	/*
1062*d00f0155Sayznaga 	 * Compute the offset within the dimm to the nearest line. This depends
1063*d00f0155Sayznaga 	 * on whether or not the bank is a front logical bank or a back logical
1064*d00f0155Sayznaga 	 * bank.
1065*d00f0155Sayznaga 	 */
1066*d00f0155Sayznaga 	base = size * bank->pos;
1067*d00f0155Sayznaga 	*off += base;
1068*d00f0155Sayznaga 
1069*d00f0155Sayznaga 	remainder = (addr - seg->base) % 64;
1070*d00f0155Sayznaga 	remainder /= 16;
1071*d00f0155Sayznaga 	*off += remainder;
1072*d00f0155Sayznaga }
1073*d00f0155Sayznaga 
1074*d00f0155Sayznaga /*
10757c478bd9Sstevel@tonic-gate  * A cache line is composed of four quadwords with the associated ECC, the
10767c478bd9Sstevel@tonic-gate  * MTag along with its associated ECC. This is depicted below:
10777c478bd9Sstevel@tonic-gate  *
10787c478bd9Sstevel@tonic-gate  * |                    Data                    |   ECC   | Mtag |MTag ECC|
10797c478bd9Sstevel@tonic-gate  *  127                                         0 8       0 2    0 3      0
10807c478bd9Sstevel@tonic-gate  *
10817c478bd9Sstevel@tonic-gate  * synd_code will be mapped as the following order to mc_get_mem_unum.
10827c478bd9Sstevel@tonic-gate  *  143                                         16        7      4        0
10837c478bd9Sstevel@tonic-gate  *
10847c478bd9Sstevel@tonic-gate  * |  Quadword  0  |  Quadword  1  |  Quadword  2  |  Quadword  3  |
10857c478bd9Sstevel@tonic-gate  *  575         432 431         288 287         144 143		   0
10867c478bd9Sstevel@tonic-gate  *
10877c478bd9Sstevel@tonic-gate  * dimm table: each bit at a cache line needs two bits to present one of
10887c478bd9Sstevel@tonic-gate  *      four dimms. So it needs 144 bytes(576 * 2 / 8). The content is in
10897c478bd9Sstevel@tonic-gate  *      big edian order, i.e. dimm_table[0] presents for bit 572 to 575.
10907c478bd9Sstevel@tonic-gate  *
10917c478bd9Sstevel@tonic-gate  * pin table: each bit at a cache line needs one byte to present pin position,
10927c478bd9Sstevel@tonic-gate  *      where max. is 230. So it needs 576 bytes. The order of table index is
10937c478bd9Sstevel@tonic-gate  *      the same as bit position at a cache line, i.e. pin_table[0] presents
10947c478bd9Sstevel@tonic-gate  *      for bit 0, Mtag ECC 0 of Quadword 3.
10957c478bd9Sstevel@tonic-gate  *
10967c478bd9Sstevel@tonic-gate  * This is a mapping from syndrome code to QuadWord Logical layout at Safari.
10977c478bd9Sstevel@tonic-gate  * Referring to Figure 3-4, Excalibur Architecture Manual.
10987c478bd9Sstevel@tonic-gate  * This table could be moved to cheetah.c if other platform teams agree with
10997c478bd9Sstevel@tonic-gate  * the bit layout at QuadWord.
11007c478bd9Sstevel@tonic-gate  */
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate static uint8_t qwordmap[] =
11037c478bd9Sstevel@tonic-gate {
11047c478bd9Sstevel@tonic-gate 16,   17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
11057c478bd9Sstevel@tonic-gate 32,   33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
11067c478bd9Sstevel@tonic-gate 48,   49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
11077c478bd9Sstevel@tonic-gate 64,   65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
11087c478bd9Sstevel@tonic-gate 80,   81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
11097c478bd9Sstevel@tonic-gate 96,   97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
11107c478bd9Sstevel@tonic-gate 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
11117c478bd9Sstevel@tonic-gate 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
11127c478bd9Sstevel@tonic-gate 7,    8,   9,  10,  11,  12,  13,  14,  15,   4,   5,   6,   0,   1,   2,   3,
11137c478bd9Sstevel@tonic-gate };
11147c478bd9Sstevel@tonic-gate 
11157c478bd9Sstevel@tonic-gate 
11167c478bd9Sstevel@tonic-gate /* ARGSUSED */
11177c478bd9Sstevel@tonic-gate static int
11187c478bd9Sstevel@tonic-gate mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf, int buflen, int *lenp)
11197c478bd9Sstevel@tonic-gate {
11207c478bd9Sstevel@tonic-gate 	int i, upper_pa, lower_pa, dimmoffset;
11217c478bd9Sstevel@tonic-gate 	int quadword, pos_cacheline, position, index, idx4dimm;
11227c478bd9Sstevel@tonic-gate 	int qwlayout = synd_code;
11237c478bd9Sstevel@tonic-gate 	short offset, data;
11247c478bd9Sstevel@tonic-gate 	char unum[UNUM_NAMLEN];
11257c478bd9Sstevel@tonic-gate 	struct dimm_info *dimmp;
11267c478bd9Sstevel@tonic-gate 	struct pin_info *pinp;
11277c478bd9Sstevel@tonic-gate 	struct bank_info *bank;
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	/*
11307c478bd9Sstevel@tonic-gate 	 * Enforce old Openboot requirement for synd code, either a single-bit
11317c478bd9Sstevel@tonic-gate 	 * code from 0..QWORD_SIZE-1 or -1 (multi-bit error).
11327c478bd9Sstevel@tonic-gate 	 */
11337c478bd9Sstevel@tonic-gate 	if (qwlayout < -1 || qwlayout >= QWORD_SIZE)
11347c478bd9Sstevel@tonic-gate 		return (EINVAL);
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	unum[0] = '\0';
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
11397c478bd9Sstevel@tonic-gate 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
11407c478bd9Sstevel@tonic-gate 
11417c478bd9Sstevel@tonic-gate 	DPRINTF(MC_GUNUM_DEBUG, ("qwlayout %d\n", qwlayout));
11427c478bd9Sstevel@tonic-gate 
11437c478bd9Sstevel@tonic-gate 	/*
11447c478bd9Sstevel@tonic-gate 	 * Scan all logical banks to get one responding to the physical
11457c478bd9Sstevel@tonic-gate 	 * address. Then compute the index to look up dimm and pin tables
1146*d00f0155Sayznaga 	 * to generate the unum.
11477c478bd9Sstevel@tonic-gate 	 */
11487c478bd9Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
11497c478bd9Sstevel@tonic-gate 	bank = (struct bank_info *)bank_head;
11507c478bd9Sstevel@tonic-gate 	while (bank != NULL) {
11517c478bd9Sstevel@tonic-gate 		int bankid, mcid, bankno_permc;
11527c478bd9Sstevel@tonic-gate 
11537c478bd9Sstevel@tonic-gate 		bankid = bank->bank_node.id;
11547c478bd9Sstevel@tonic-gate 		bankno_permc = bankid % NBANKS;
11557c478bd9Sstevel@tonic-gate 		mcid = bankid / NBANKS;
11567c478bd9Sstevel@tonic-gate 
11577c478bd9Sstevel@tonic-gate 		/*
11587c478bd9Sstevel@tonic-gate 		 * The Address Decoding logic decodes the different fields
1159*d00f0155Sayznaga 		 * in the Memory Address Decoding register to determine
1160*d00f0155Sayznaga 		 * whether a particular logical bank should respond to a
11617c478bd9Sstevel@tonic-gate 		 * physical address.
11627c478bd9Sstevel@tonic-gate 		 */
11637c478bd9Sstevel@tonic-gate 		if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) |
11647c478bd9Sstevel@tonic-gate 		    bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) {
11657c478bd9Sstevel@tonic-gate 			bank = (struct bank_info *)bank->bank_node.next;
11667c478bd9Sstevel@tonic-gate 			continue;
11677c478bd9Sstevel@tonic-gate 		}
11687c478bd9Sstevel@tonic-gate 
11697c478bd9Sstevel@tonic-gate 		dimmoffset = (bankno_permc % NDGRPS) * NDIMMS;
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 		dimmp = (struct dimm_info *)bank->dimminfop;
11727c478bd9Sstevel@tonic-gate 		ASSERT(dimmp != NULL);
11737c478bd9Sstevel@tonic-gate 
11747c478bd9Sstevel@tonic-gate 		if ((qwlayout >= 0) && (qwlayout < QWORD_SIZE)) {
11757c478bd9Sstevel@tonic-gate 			/*
11767c478bd9Sstevel@tonic-gate 			 * single-bit error handling, we can identify specific
11777c478bd9Sstevel@tonic-gate 			 * DIMM.
11787c478bd9Sstevel@tonic-gate 			 */
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 			pinp = (struct pin_info *)&dimmp->data[0];
11817c478bd9Sstevel@tonic-gate 
11827c478bd9Sstevel@tonic-gate 			if (!dimmp->sym_flag)
11837c478bd9Sstevel@tonic-gate 				pinp++;
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate 			quadword = (paddr & 0x3f) / 16;
11867c478bd9Sstevel@tonic-gate 			/* or quadword = (paddr >> 4) % 4; */
1187*d00f0155Sayznaga 			pos_cacheline = ((3 - quadword) * QWORD_SIZE) +
11887c478bd9Sstevel@tonic-gate 			    qwordmap[qwlayout];
11897c478bd9Sstevel@tonic-gate 			position = 575 - pos_cacheline;
11907c478bd9Sstevel@tonic-gate 			index = position * 2 / 8;
11917c478bd9Sstevel@tonic-gate 			offset = position % 4;
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 			/*
1194*d00f0155Sayznaga 			 * Trade-off: We couldn't add pin number to
1195*d00f0155Sayznaga 			 * unum string because statistic number
11967c478bd9Sstevel@tonic-gate 			 * pumps up at the corresponding dimm not pin.
11977c478bd9Sstevel@tonic-gate 			 * (void) sprintf(unum, "Pin %1u ", (uint_t)
11987c478bd9Sstevel@tonic-gate 			 * pinp->pintable[pos_cacheline]);
11997c478bd9Sstevel@tonic-gate 			 */
12007c478bd9Sstevel@tonic-gate 			DPRINTF(MC_GUNUM_DEBUG, ("Pin number %1u\n",
12017c478bd9Sstevel@tonic-gate 			    (uint_t)pinp->pintable[pos_cacheline]));
12027c478bd9Sstevel@tonic-gate 			data = pinp->dimmtable[index];
12037c478bd9Sstevel@tonic-gate 			idx4dimm = (data >> ((3 - offset) * 2)) & 3;
12047c478bd9Sstevel@tonic-gate 
12057c478bd9Sstevel@tonic-gate 			(void) strncpy(unum,
12067c478bd9Sstevel@tonic-gate 			    (char *)dimmp->label[dimmoffset + idx4dimm],
12077c478bd9Sstevel@tonic-gate 			    UNUM_NAMLEN);
12087c478bd9Sstevel@tonic-gate 			DPRINTF(MC_GUNUM_DEBUG, ("unum %s\n", unum));
12097c478bd9Sstevel@tonic-gate 			/*
12107c478bd9Sstevel@tonic-gate 			 * platform hook for adding label information to unum.
12117c478bd9Sstevel@tonic-gate 			 */
12127c478bd9Sstevel@tonic-gate 			mc_add_mem_unum_label(unum, mcid, bankno_permc,
12137c478bd9Sstevel@tonic-gate 			    idx4dimm);
12147c478bd9Sstevel@tonic-gate 		} else {
12157c478bd9Sstevel@tonic-gate 			char *p = unum;
12167c478bd9Sstevel@tonic-gate 			size_t res = UNUM_NAMLEN;
12177c478bd9Sstevel@tonic-gate 
12187c478bd9Sstevel@tonic-gate 			/*
12197c478bd9Sstevel@tonic-gate 			 * multi-bit error handling, we can only identify
12207c478bd9Sstevel@tonic-gate 			 * bank of DIMMs.
12217c478bd9Sstevel@tonic-gate 			 */
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 			for (i = 0; (i < NDIMMS) && (res > 0); i++) {
12247c478bd9Sstevel@tonic-gate 				(void) snprintf(p, res, "%s%s",
12257c478bd9Sstevel@tonic-gate 				    i == 0 ? "" : " ",
12267c478bd9Sstevel@tonic-gate 				    (char *)dimmp->label[dimmoffset + i]);
12277c478bd9Sstevel@tonic-gate 				res -= strlen(p);
12287c478bd9Sstevel@tonic-gate 				p += strlen(p);
12297c478bd9Sstevel@tonic-gate 			}
12307c478bd9Sstevel@tonic-gate 
12317c478bd9Sstevel@tonic-gate 			/*
12327c478bd9Sstevel@tonic-gate 			 * platform hook for adding label information
12337c478bd9Sstevel@tonic-gate 			 * to unum.
12347c478bd9Sstevel@tonic-gate 			 */
12357c478bd9Sstevel@tonic-gate 			mc_add_mem_unum_label(unum, mcid, bankno_permc, -1);
12367c478bd9Sstevel@tonic-gate 		}
12377c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
12387c478bd9Sstevel@tonic-gate 		if ((strlen(unum) >= UNUM_NAMLEN) ||
12397c478bd9Sstevel@tonic-gate 		    (strlen(unum) >= buflen)) {
12407c478bd9Sstevel@tonic-gate 			return (ENOSPC);
12417c478bd9Sstevel@tonic-gate 		} else {
12427c478bd9Sstevel@tonic-gate 			(void) strncpy(buf, unum, buflen);
12437c478bd9Sstevel@tonic-gate 			*lenp = strlen(buf);
12447c478bd9Sstevel@tonic-gate 			return (0);
12457c478bd9Sstevel@tonic-gate 		}
1246*d00f0155Sayznaga 	}	/* end of while loop for logical bank list */
12477c478bd9Sstevel@tonic-gate 
12487c478bd9Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
12497c478bd9Sstevel@tonic-gate 	return (ENXIO);
12507c478bd9Sstevel@tonic-gate }
12517c478bd9Sstevel@tonic-gate 
1252*d00f0155Sayznaga /* ARGSUSED */
1253*d00f0155Sayznaga static int
1254*d00f0155Sayznaga mc_get_mem_offset(uint64_t paddr, uint64_t *offp)
1255*d00f0155Sayznaga {
1256*d00f0155Sayznaga 	int upper_pa, lower_pa;
1257*d00f0155Sayznaga 	struct bank_info *bank;
1258*d00f0155Sayznaga 	struct seg_info *seg;
1259*d00f0155Sayznaga 
1260*d00f0155Sayznaga 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
1261*d00f0155Sayznaga 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
1262*d00f0155Sayznaga 
1263*d00f0155Sayznaga 	/*
1264*d00f0155Sayznaga 	 * Scan all logical banks to get one responding to the physical
1265*d00f0155Sayznaga 	 * address.
1266*d00f0155Sayznaga 	 */
1267*d00f0155Sayznaga 	mutex_enter(&mcdatamutex);
1268*d00f0155Sayznaga 	bank = (struct bank_info *)bank_head;
1269*d00f0155Sayznaga 	while (bank != NULL) {
1270*d00f0155Sayznaga 		/*
1271*d00f0155Sayznaga 		 * The Address Decoding logic decodes the different fields
1272*d00f0155Sayznaga 		 * in the Memory Address Decoding register to determine
1273*d00f0155Sayznaga 		 * whether a particular logical bank should respond to a
1274*d00f0155Sayznaga 		 * physical address.
1275*d00f0155Sayznaga 		 */
1276*d00f0155Sayznaga 		if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) |
1277*d00f0155Sayznaga 		    bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) {
1278*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
1279*d00f0155Sayznaga 			continue;
1280*d00f0155Sayznaga 		}
1281*d00f0155Sayznaga 
1282*d00f0155Sayznaga 		seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head);
1283*d00f0155Sayznaga 		ASSERT(seg != NULL);
1284*d00f0155Sayznaga 		ASSERT(paddr >= seg->base);
1285*d00f0155Sayznaga 
1286*d00f0155Sayznaga 		mc_addr_to_offset(seg, bank, paddr, offp);
1287*d00f0155Sayznaga 
1288*d00f0155Sayznaga 		mutex_exit(&mcdatamutex);
1289*d00f0155Sayznaga 		return (0);
1290*d00f0155Sayznaga 	}
1291*d00f0155Sayznaga 
1292*d00f0155Sayznaga 	mutex_exit(&mcdatamutex);
1293*d00f0155Sayznaga 	return (ENXIO);
1294*d00f0155Sayznaga }
1295*d00f0155Sayznaga 
1296*d00f0155Sayznaga /*
1297*d00f0155Sayznaga  * Translate a DIMM <id, offset> pair to a physical address.
1298*d00f0155Sayznaga  */
1299*d00f0155Sayznaga static int
1300*d00f0155Sayznaga mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr)
1301*d00f0155Sayznaga {
1302*d00f0155Sayznaga 	struct seg_info *seg;
1303*d00f0155Sayznaga 	struct bank_info *bank;
1304*d00f0155Sayznaga 	int first_seg_id;
1305*d00f0155Sayznaga 	int i, found;
1306*d00f0155Sayznaga 
1307*d00f0155Sayznaga 	ASSERT(sid != NULL);
1308*d00f0155Sayznaga 
1309*d00f0155Sayznaga 	mutex_enter(&mcdatamutex);
1310*d00f0155Sayznaga 
1311*d00f0155Sayznaga 	rw_enter(&mcdimmsids_rw, RW_READER);
1312*d00f0155Sayznaga 
1313*d00f0155Sayznaga 	/*
1314*d00f0155Sayznaga 	 * If DIMM serial ids have not been cached yet, tell the
1315*d00f0155Sayznaga 	 * caller to try again.
1316*d00f0155Sayznaga 	 */
1317*d00f0155Sayznaga 	if (mc_dimm_sids == NULL) {
1318*d00f0155Sayznaga 		rw_exit(&mcdimmsids_rw);
1319*d00f0155Sayznaga 		return (EAGAIN);
1320*d00f0155Sayznaga 	}
1321*d00f0155Sayznaga 
1322*d00f0155Sayznaga 	for (i = 0; i < max_entries; i++) {
1323*d00f0155Sayznaga 		if (mc_dimm_sids[i].mcid == mcid)
1324*d00f0155Sayznaga 			break;
1325*d00f0155Sayznaga 	}
1326*d00f0155Sayznaga 
1327*d00f0155Sayznaga 	if (i == max_entries) {
1328*d00f0155Sayznaga 		rw_exit(&mcdimmsids_rw);
1329*d00f0155Sayznaga 		mutex_exit(&mcdatamutex);
1330*d00f0155Sayznaga 		return (ENODEV);
1331*d00f0155Sayznaga 	}
1332*d00f0155Sayznaga 
1333*d00f0155Sayznaga 	first_seg_id = mc_dimm_sids[i].seg_id;
1334*d00f0155Sayznaga 
1335*d00f0155Sayznaga 	seg = (struct seg_info *)mc_node_get(first_seg_id, seg_head);
1336*d00f0155Sayznaga 
1337*d00f0155Sayznaga 	rw_exit(&mcdimmsids_rw);
1338*d00f0155Sayznaga 
1339*d00f0155Sayznaga 	if (seg == NULL) {
1340*d00f0155Sayznaga 		mutex_exit(&mcdatamutex);
1341*d00f0155Sayznaga 		return (ENODEV);
1342*d00f0155Sayznaga 	}
1343*d00f0155Sayznaga 
1344*d00f0155Sayznaga 	found = 0;
1345*d00f0155Sayznaga 
1346*d00f0155Sayznaga 	for (bank = seg->hb_inseg; bank; bank = bank->n_inseg) {
1347*d00f0155Sayznaga 		ASSERT(bank->valid);
1348*d00f0155Sayznaga 
1349*d00f0155Sayznaga 		for (i = 0; i < NDIMMS; i++) {
1350*d00f0155Sayznaga 			if (strncmp((char *)bank->dimmsidp[i], sid,
1351*d00f0155Sayznaga 			    DIMM_SERIAL_ID_LEN)  == 0)
1352*d00f0155Sayznaga 				break;
1353*d00f0155Sayznaga 		}
1354*d00f0155Sayznaga 
1355*d00f0155Sayznaga 		if (i == NDIMMS)
1356*d00f0155Sayznaga 			continue;
1357*d00f0155Sayznaga 
1358*d00f0155Sayznaga 		if (mc_offset_to_addr(seg, bank, off, paddr) == -1)
1359*d00f0155Sayznaga 			continue;
1360*d00f0155Sayznaga 		found = 1;
1361*d00f0155Sayznaga 		break;
1362*d00f0155Sayznaga 	}
1363*d00f0155Sayznaga 
1364*d00f0155Sayznaga 	if (found) {
1365*d00f0155Sayznaga 		mutex_exit(&mcdatamutex);
1366*d00f0155Sayznaga 		return (0);
1367*d00f0155Sayznaga 	}
1368*d00f0155Sayznaga 
1369*d00f0155Sayznaga 	/*
1370*d00f0155Sayznaga 	 * If a bank wasn't found, it may be in another segment.
1371*d00f0155Sayznaga 	 * This can happen if the different logical banks of an MC
1372*d00f0155Sayznaga 	 * have different interleave factors.  To deal with this
1373*d00f0155Sayznaga 	 * possibility, we'll do a brute-force search for banks
1374*d00f0155Sayznaga 	 * for this MC with a different seg id then above.
1375*d00f0155Sayznaga 	 */
1376*d00f0155Sayznaga 	bank = (struct bank_info *)bank_head;
1377*d00f0155Sayznaga 	while (bank != NULL) {
1378*d00f0155Sayznaga 
1379*d00f0155Sayznaga 		if (!bank->valid) {
1380*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
1381*d00f0155Sayznaga 			continue;
1382*d00f0155Sayznaga 		}
1383*d00f0155Sayznaga 
1384*d00f0155Sayznaga 		if (bank->bank_node.id / NBANKS != mcid) {
1385*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
1386*d00f0155Sayznaga 			continue;
1387*d00f0155Sayznaga 		}
1388*d00f0155Sayznaga 
1389*d00f0155Sayznaga 		/* Ignore banks in the segment we looked in above. */
1390*d00f0155Sayznaga 		if (bank->seg_id == mc_dimm_sids[i].seg_id) {
1391*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
1392*d00f0155Sayznaga 			continue;
1393*d00f0155Sayznaga 		}
1394*d00f0155Sayznaga 
1395*d00f0155Sayznaga 		for (i = 0; i < NDIMMS; i++) {
1396*d00f0155Sayznaga 			if (strncmp((char *)bank->dimmsidp[i], sid,
1397*d00f0155Sayznaga 			    DIMM_SERIAL_ID_LEN)  == 0)
1398*d00f0155Sayznaga 				break;
1399*d00f0155Sayznaga 		}
1400*d00f0155Sayznaga 
1401*d00f0155Sayznaga 		if (i == NDIMMS) {
1402*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
1403*d00f0155Sayznaga 			continue;
1404*d00f0155Sayznaga 		}
1405*d00f0155Sayznaga 
1406*d00f0155Sayznaga 		seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head);
1407*d00f0155Sayznaga 
1408*d00f0155Sayznaga 		if (mc_offset_to_addr(seg, bank, off, paddr) == -1) {
1409*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
1410*d00f0155Sayznaga 			continue;
1411*d00f0155Sayznaga 		}
1412*d00f0155Sayznaga 
1413*d00f0155Sayznaga 		found = 1;
1414*d00f0155Sayznaga 		break;
1415*d00f0155Sayznaga 	}
1416*d00f0155Sayznaga 
1417*d00f0155Sayznaga 	mutex_exit(&mcdatamutex);
1418*d00f0155Sayznaga 
1419*d00f0155Sayznaga 	if (found)
1420*d00f0155Sayznaga 		return (0);
1421*d00f0155Sayznaga 	else
1422*d00f0155Sayznaga 		return (ENOENT);
1423*d00f0155Sayznaga }
1424*d00f0155Sayznaga 
14257c478bd9Sstevel@tonic-gate static int
14267c478bd9Sstevel@tonic-gate mc_get_mem_info(int synd_code, uint64_t paddr,
14277c478bd9Sstevel@tonic-gate     uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
14287c478bd9Sstevel@tonic-gate     int *segsp, int *banksp, int *mcidp)
14297c478bd9Sstevel@tonic-gate {
14307c478bd9Sstevel@tonic-gate 	int upper_pa, lower_pa;
14317c478bd9Sstevel@tonic-gate 	struct bank_info *bankp;
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate 	if (synd_code < -1 || synd_code >= QWORD_SIZE)
14347c478bd9Sstevel@tonic-gate 		return (EINVAL);
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
14377c478bd9Sstevel@tonic-gate 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
14387c478bd9Sstevel@tonic-gate 
14397c478bd9Sstevel@tonic-gate 	/*
14407c478bd9Sstevel@tonic-gate 	 * Scan all logical banks to get one responding to the physical
14417c478bd9Sstevel@tonic-gate 	 * address.
14427c478bd9Sstevel@tonic-gate 	 */
14437c478bd9Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
14447c478bd9Sstevel@tonic-gate 	bankp = (struct bank_info *)bank_head;
14457c478bd9Sstevel@tonic-gate 	while (bankp != NULL) {
14467c478bd9Sstevel@tonic-gate 		struct seg_info *segp;
14477c478bd9Sstevel@tonic-gate 		int bankid, mcid;
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 		bankid = bankp->bank_node.id;
14507c478bd9Sstevel@tonic-gate 		mcid = bankid / NBANKS;
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 		/*
14537c478bd9Sstevel@tonic-gate 		 * The Address Decoding logic decodes the different fields
14547c478bd9Sstevel@tonic-gate 		 * in the Memory Address Decoding register to determine
1455*d00f0155Sayznaga 		 * whether a particular logical bank should respond to a
14567c478bd9Sstevel@tonic-gate 		 * physical address.
14577c478bd9Sstevel@tonic-gate 		 */
14587c478bd9Sstevel@tonic-gate 		if ((!bankp->valid) || ((~(~(upper_pa ^ bankp->um) |
14597c478bd9Sstevel@tonic-gate 		    bankp->uk)) || (~(~(lower_pa ^ bankp->lm) | bankp->lk)))) {
14607c478bd9Sstevel@tonic-gate 			bankp = (struct bank_info *)bankp->bank_node.next;
14617c478bd9Sstevel@tonic-gate 			continue;
14627c478bd9Sstevel@tonic-gate 		}
14637c478bd9Sstevel@tonic-gate 
14647c478bd9Sstevel@tonic-gate 		/*
14657c478bd9Sstevel@tonic-gate 		 * Get the corresponding segment.
14667c478bd9Sstevel@tonic-gate 		 */
14677c478bd9Sstevel@tonic-gate 		if ((segp = (struct seg_info *)mc_node_get(bankp->seg_id,
14687c478bd9Sstevel@tonic-gate 		    seg_head)) == NULL) {
14697c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
14707c478bd9Sstevel@tonic-gate 			return (EFAULT);
14717c478bd9Sstevel@tonic-gate 		}
14727c478bd9Sstevel@tonic-gate 
14737c478bd9Sstevel@tonic-gate 		*mem_sizep = memsize;
14747c478bd9Sstevel@tonic-gate 		*seg_sizep = segp->size;
14757c478bd9Sstevel@tonic-gate 		*bank_sizep = bankp->size;
14767c478bd9Sstevel@tonic-gate 		*segsp = nsegments;
14777c478bd9Sstevel@tonic-gate 		*banksp = segp->nbanks;
14787c478bd9Sstevel@tonic-gate 		*mcidp = mcid;
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate 		return (0);
14837c478bd9Sstevel@tonic-gate 
1484*d00f0155Sayznaga 	}	/* end of while loop for logical bank list */
14857c478bd9Sstevel@tonic-gate 
14867c478bd9Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
14877c478bd9Sstevel@tonic-gate 	return (ENXIO);
14887c478bd9Sstevel@tonic-gate }
14897c478bd9Sstevel@tonic-gate 
14907c478bd9Sstevel@tonic-gate /*
14917c478bd9Sstevel@tonic-gate  * Construct lists for an enabled MC where size of memory is 0.
14927c478bd9Sstevel@tonic-gate  * The lists are connected as follows:
14937c478bd9Sstevel@tonic-gate  * Attached MC -> device group list -> device list(per devgrp).
14947c478bd9Sstevel@tonic-gate  */
14957c478bd9Sstevel@tonic-gate static void
14967c478bd9Sstevel@tonic-gate mc_construct(int mc_id, void *dimminfop)
14977c478bd9Sstevel@tonic-gate {
14987c478bd9Sstevel@tonic-gate 	int i, j, idx, dmidx;
14997c478bd9Sstevel@tonic-gate 	struct mctrl_info *mctrl;
15007c478bd9Sstevel@tonic-gate 	struct dgrp_info *dgrp;
15017c478bd9Sstevel@tonic-gate 	struct device_info *dev;
15027c478bd9Sstevel@tonic-gate 	struct	dimm_info *dimmp = (struct  dimm_info *)dimminfop;
15037c478bd9Sstevel@tonic-gate 
15047c478bd9Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
15057c478bd9Sstevel@tonic-gate 	/* allocate for mctrl_info and bank_info */
15067c478bd9Sstevel@tonic-gate 	if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id,
15077c478bd9Sstevel@tonic-gate 	    mctrl_head)) != NULL) {
15087c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "mc_construct: mctrl %d exists\n", mc_id);
15097c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
15107c478bd9Sstevel@tonic-gate 		return;
15117c478bd9Sstevel@tonic-gate 	}
15127c478bd9Sstevel@tonic-gate 
15137c478bd9Sstevel@tonic-gate 	mctrl = kmem_zalloc(sizeof (struct mctrl_info), KM_SLEEP);
15147c478bd9Sstevel@tonic-gate 
15157c478bd9Sstevel@tonic-gate 	/*
15167c478bd9Sstevel@tonic-gate 	 * If dimminfop is NULL, the Memory Controller is disable, and
15177c478bd9Sstevel@tonic-gate 	 * the number of device group will be zero.
15187c478bd9Sstevel@tonic-gate 	 */
15197c478bd9Sstevel@tonic-gate 	if (dimminfop == NULL) {
15207c478bd9Sstevel@tonic-gate 		mctrl->mctrl_node.id = mc_id;
15217c478bd9Sstevel@tonic-gate 		mctrl->ndevgrps = 0;
15227c478bd9Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
15237c478bd9Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
15247c478bd9Sstevel@tonic-gate 		return;
15257c478bd9Sstevel@tonic-gate 	}
15267c478bd9Sstevel@tonic-gate 
15277c478bd9Sstevel@tonic-gate 	/* add the entry on dgrp_info list */
15287c478bd9Sstevel@tonic-gate 	for (i = 0; i < NDGRPS; i++) {
15297c478bd9Sstevel@tonic-gate 		idx = mc_id * NDGRPS + i;
15307c478bd9Sstevel@tonic-gate 		mctrl->devgrpids[i] = idx;
15317c478bd9Sstevel@tonic-gate 		if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head))
15327c478bd9Sstevel@tonic-gate 		    != NULL) {
15337c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "mc_construct: devgrp %d exists\n",
15347c478bd9Sstevel@tonic-gate 			    idx);
15357c478bd9Sstevel@tonic-gate 			continue;
15367c478bd9Sstevel@tonic-gate 		}
15377c478bd9Sstevel@tonic-gate 
15387c478bd9Sstevel@tonic-gate 		dgrp = kmem_zalloc(sizeof (struct dgrp_info), KM_SLEEP);
15397c478bd9Sstevel@tonic-gate 
15407c478bd9Sstevel@tonic-gate 		/* add the entry on device_info list */
15417c478bd9Sstevel@tonic-gate 		for (j = 0; j < NDIMMS; j++) {
15427c478bd9Sstevel@tonic-gate 			dmidx = idx * NDIMMS + j;
15437c478bd9Sstevel@tonic-gate 			dgrp->deviceids[j] = dmidx;
15447c478bd9Sstevel@tonic-gate 			if ((dev = (struct device_info *)
15457c478bd9Sstevel@tonic-gate 			    mc_node_get(dmidx, device_head)) != NULL) {
15467c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "mc_construct: device %d "
15477c478bd9Sstevel@tonic-gate 				    "exists\n", dmidx);
15487c478bd9Sstevel@tonic-gate 				continue;
15497c478bd9Sstevel@tonic-gate 			}
15507c478bd9Sstevel@tonic-gate 			dev = kmem_zalloc(sizeof (struct device_info),
15517c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
15527c478bd9Sstevel@tonic-gate 			dev->dev_node.id = dmidx;
15537c478bd9Sstevel@tonic-gate 			dev->size = 0;
15547c478bd9Sstevel@tonic-gate 			(void) strncpy(dev->label, (char *)
15557c478bd9Sstevel@tonic-gate 			    dimmp->label[i * NDIMMS + j], MAX_DEVLEN);
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 			mc_node_add((mc_dlist_t *)dev, &device_head,
15587c478bd9Sstevel@tonic-gate 			    &device_tail);
15597c478bd9Sstevel@tonic-gate 		}	/* for loop for constructing device_info */
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate 		dgrp->dgrp_node.id = idx;
15627c478bd9Sstevel@tonic-gate 		dgrp->ndevices = NDIMMS;
15637c478bd9Sstevel@tonic-gate 		dgrp->size = 0;
15647c478bd9Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)dgrp, &dgrp_head, &dgrp_tail);
15657c478bd9Sstevel@tonic-gate 
15667c478bd9Sstevel@tonic-gate 	}	/* end of for loop for constructing dgrp_info list */
15677c478bd9Sstevel@tonic-gate 
15687c478bd9Sstevel@tonic-gate 	mctrl->mctrl_node.id = mc_id;
15697c478bd9Sstevel@tonic-gate 	mctrl->ndevgrps = NDGRPS;
15707c478bd9Sstevel@tonic-gate 	mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
15717c478bd9Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
15727c478bd9Sstevel@tonic-gate }
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate /*
15757c478bd9Sstevel@tonic-gate  * Construct lists for Memory Configuration at logical viewpoint.
15767c478bd9Sstevel@tonic-gate  *
15777c478bd9Sstevel@tonic-gate  * Retrieve information from Memory Address Decoding Register and set up
15787c478bd9Sstevel@tonic-gate  * bank and segment lists. Link bank to its corresponding device group, and
15797c478bd9Sstevel@tonic-gate  * update size of device group and devices. Also connect bank to the segment.
15807c478bd9Sstevel@tonic-gate  *
15817c478bd9Sstevel@tonic-gate  * Memory Address Decoding Register
15827c478bd9Sstevel@tonic-gate  * -------------------------------------------------------------------------
15837c478bd9Sstevel@tonic-gate  * |63|62    53|52      41|40  37|36     20|19 18|17  14|13 12|11  8|7     0|
15847c478bd9Sstevel@tonic-gate  * |-----------|----------|------|---------|-----|------|-----|-----|-------|
15857c478bd9Sstevel@tonic-gate  * |V |    -   |    UK    |   -  |    UM   |  -  |  LK  |  -  | LM  |   -   |
15867c478bd9Sstevel@tonic-gate  * -------------------------------------------------------------------------
15877c478bd9Sstevel@tonic-gate  *
15887c478bd9Sstevel@tonic-gate  */
15897c478bd9Sstevel@tonic-gate 
15907c478bd9Sstevel@tonic-gate static int
15917c478bd9Sstevel@tonic-gate mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop)
15927c478bd9Sstevel@tonic-gate {
15937c478bd9Sstevel@tonic-gate 	int i, dmidx, idx;
15947c478bd9Sstevel@tonic-gate 	uint32_t ifactor;
15957c478bd9Sstevel@tonic-gate 	int status = 0;
15967c478bd9Sstevel@tonic-gate 	uint64_t size, base;
15977c478bd9Sstevel@tonic-gate 	struct seg_info *seg_curr;
15987c478bd9Sstevel@tonic-gate 	struct bank_info *bank_curr;
15997c478bd9Sstevel@tonic-gate 	struct dgrp_info *dgrp;
16007c478bd9Sstevel@tonic-gate 	struct device_info *dev;
16017c478bd9Sstevel@tonic-gate 	union {
16027c478bd9Sstevel@tonic-gate 		struct {
16037c478bd9Sstevel@tonic-gate 			uint64_t valid	: 1;
16047c478bd9Sstevel@tonic-gate 			uint64_t resrv1	: 10;
16057c478bd9Sstevel@tonic-gate 			uint64_t uk	: 12;
16067c478bd9Sstevel@tonic-gate 			uint64_t resrv2	: 4;
16077c478bd9Sstevel@tonic-gate 			uint64_t um	: 17;
16087c478bd9Sstevel@tonic-gate 			uint64_t resrv3	: 2;
16097c478bd9Sstevel@tonic-gate 			uint64_t lk	: 4;
16107c478bd9Sstevel@tonic-gate 			uint64_t resrv4	: 2;
16117c478bd9Sstevel@tonic-gate 			uint64_t lm	: 4;
16127c478bd9Sstevel@tonic-gate 			uint64_t resrv5	: 8;
16137c478bd9Sstevel@tonic-gate 		} _s;
16147c478bd9Sstevel@tonic-gate 		uint64_t madreg;
16157c478bd9Sstevel@tonic-gate 	} mcreg;
16167c478bd9Sstevel@tonic-gate 
16177c478bd9Sstevel@tonic-gate 	mcreg.madreg = reg;
16187c478bd9Sstevel@tonic-gate 
16197c478bd9Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: mc_id %d, bank num "
1620f47a9c50Smathue 	    "%d, reg 0x%lx\n", mc_id, bank_no, reg));
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate 	/* add the entry on bank_info list */
16237c478bd9Sstevel@tonic-gate 	idx = mc_id * NBANKS + bank_no;
16247c478bd9Sstevel@tonic-gate 
16257c478bd9Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
16267c478bd9Sstevel@tonic-gate 	if ((bank_curr = (struct bank_info *)mc_node_get(idx, bank_head))
16277c478bd9Sstevel@tonic-gate 	    != NULL) {
16287c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "mlayout_add: bank %d exists\n", bank_no);
16297c478bd9Sstevel@tonic-gate 		goto exit;
16307c478bd9Sstevel@tonic-gate 	}
16317c478bd9Sstevel@tonic-gate 
16327c478bd9Sstevel@tonic-gate 	bank_curr = kmem_zalloc(sizeof (struct bank_info), KM_SLEEP);
16337c478bd9Sstevel@tonic-gate 	bank_curr->bank_node.id = idx;
16347c478bd9Sstevel@tonic-gate 	bank_curr->valid = mcreg._s.valid;
16357c478bd9Sstevel@tonic-gate 	bank_curr->dimminfop = dimminfop;
16367c478bd9Sstevel@tonic-gate 
16377c478bd9Sstevel@tonic-gate 	if (!mcreg._s.valid) {
16387c478bd9Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
16397c478bd9Sstevel@tonic-gate 		goto exit;
16407c478bd9Sstevel@tonic-gate 	}
16417c478bd9Sstevel@tonic-gate 
16427c478bd9Sstevel@tonic-gate 	/*
16437c478bd9Sstevel@tonic-gate 	 * size of a logical bank = size of segment / interleave factor
16447c478bd9Sstevel@tonic-gate 	 * This fomula is not only working for regular configuration,
16457c478bd9Sstevel@tonic-gate 	 * i.e. number of banks at a segment equals to the max
16467c478bd9Sstevel@tonic-gate 	 * interleave factor, but also for special case, say 3 bank
16477c478bd9Sstevel@tonic-gate 	 * interleave. One bank is 2 way interleave and other two are
16487c478bd9Sstevel@tonic-gate 	 * 4 way. So the sizes of banks are size of segment/2 and /4
16497c478bd9Sstevel@tonic-gate 	 * respectively.
16507c478bd9Sstevel@tonic-gate 	 */
16517c478bd9Sstevel@tonic-gate 	ifactor = (mcreg._s.lk ^ 0xF) + 1;
16527c478bd9Sstevel@tonic-gate 	size = (((mcreg._s.uk & 0x3FF) + 1) * 0x4000000) / ifactor;
16537c478bd9Sstevel@tonic-gate 	base = mcreg._s.um & ~mcreg._s.uk;
16547c478bd9Sstevel@tonic-gate 	base <<= MADR_UPA_SHIFT;
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate 	bank_curr->uk = mcreg._s.uk;
16577c478bd9Sstevel@tonic-gate 	bank_curr->um = mcreg._s.um;
16587c478bd9Sstevel@tonic-gate 	bank_curr->lk = mcreg._s.lk;
16597c478bd9Sstevel@tonic-gate 	bank_curr->lm = mcreg._s.lm;
16607c478bd9Sstevel@tonic-gate 	bank_curr->size = size;
16617c478bd9Sstevel@tonic-gate 
1662*d00f0155Sayznaga 	/*
1663*d00f0155Sayznaga 	 * The bank's position depends on which halves of the DIMMs it consists
1664*d00f0155Sayznaga 	 * of. The front-side halves of the 4 DIMMs constitute the front bank
1665*d00f0155Sayznaga 	 * and the back-side halves constitute the back bank. Bank numbers
1666*d00f0155Sayznaga 	 * 0 and 1 are front-side banks and bank numbers 2 and 3 are back side
1667*d00f0155Sayznaga 	 * banks.
1668*d00f0155Sayznaga 	 */
1669*d00f0155Sayznaga 	bank_curr->pos = bank_no >> 1;
1670*d00f0155Sayznaga 	ASSERT((bank_curr->pos == 0) || (bank_curr->pos == 1));
1671*d00f0155Sayznaga 
16727c478bd9Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add 3: logical bank num %d, "
1673f47a9c50Smathue 	"lk 0x%x uk 0x%x um 0x%x ifactor 0x%x size 0x%lx base 0x%lx\n",
1674f47a9c50Smathue 	    idx, mcreg._s.lk, mcreg._s.uk, mcreg._s.um, ifactor, size, base));
16757c478bd9Sstevel@tonic-gate 
16767c478bd9Sstevel@tonic-gate 	/* connect the entry and update the size on dgrp_info list */
16777c478bd9Sstevel@tonic-gate 	idx = mc_id * NDGRPS + (bank_no % NDGRPS);
16787c478bd9Sstevel@tonic-gate 	if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head)) == NULL) {
16797c478bd9Sstevel@tonic-gate 		/* all avaiable dgrp should be linked at mc_construct */
16807c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "mlayout_add: dgrp %d doesn't exist\n", idx);
16817c478bd9Sstevel@tonic-gate 		kmem_free(bank_curr, sizeof (struct bank_info));
16827c478bd9Sstevel@tonic-gate 		status = -1;
16837c478bd9Sstevel@tonic-gate 		goto exit;
16847c478bd9Sstevel@tonic-gate 	}
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 	bank_curr->devgrp_id = idx;
16877c478bd9Sstevel@tonic-gate 	dgrp->size += size;
16887c478bd9Sstevel@tonic-gate 
16897c478bd9Sstevel@tonic-gate 	/* Update the size of entry on device_info list */
16907c478bd9Sstevel@tonic-gate 	for (i = 0; i < NDIMMS; i++) {
16917c478bd9Sstevel@tonic-gate 		dmidx = dgrp->dgrp_node.id * NDIMMS + i;
16927c478bd9Sstevel@tonic-gate 		dgrp->deviceids[i] = dmidx;
16937c478bd9Sstevel@tonic-gate 
16947c478bd9Sstevel@tonic-gate 		/* avaiable device should be linked at mc_construct */
16957c478bd9Sstevel@tonic-gate 		if ((dev = (struct device_info *)mc_node_get(dmidx,
16967c478bd9Sstevel@tonic-gate 		    device_head)) == NULL) {
16977c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_add:dev %d doesn't exist\n",
16987c478bd9Sstevel@tonic-gate 			    dmidx);
16997c478bd9Sstevel@tonic-gate 			kmem_free(bank_curr, sizeof (struct bank_info));
17007c478bd9Sstevel@tonic-gate 			status = -1;
17017c478bd9Sstevel@tonic-gate 			goto exit;
17027c478bd9Sstevel@tonic-gate 		}
17037c478bd9Sstevel@tonic-gate 
17047c478bd9Sstevel@tonic-gate 		dev->size += (size / NDIMMS);
17057c478bd9Sstevel@tonic-gate 
1706f47a9c50Smathue 		DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add DIMM:id %d, size %lu\n",
17077c478bd9Sstevel@tonic-gate 		    dmidx, size));
17087c478bd9Sstevel@tonic-gate 	}
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 	/*
17117c478bd9Sstevel@tonic-gate 	 * Get the segment by matching the base address, link this bank
17127c478bd9Sstevel@tonic-gate 	 * to the segment. If not matched, allocate a new segment and
17137c478bd9Sstevel@tonic-gate 	 * add it at segment list.
17147c478bd9Sstevel@tonic-gate 	 */
17157c478bd9Sstevel@tonic-gate 	if (seg_curr = seg_match_base(base)) {
17167c478bd9Sstevel@tonic-gate 		seg_curr->nbanks++;
17177c478bd9Sstevel@tonic-gate 		seg_curr->size += size;
17187c478bd9Sstevel@tonic-gate 		if (ifactor > seg_curr->ifactor)
17197c478bd9Sstevel@tonic-gate 			seg_curr->ifactor = ifactor;
17207c478bd9Sstevel@tonic-gate 		bank_curr->seg_id = seg_curr->seg_node.id;
17217c478bd9Sstevel@tonic-gate 	} else {
17227c478bd9Sstevel@tonic-gate 		seg_curr = (struct seg_info *)
17237c478bd9Sstevel@tonic-gate 		kmem_zalloc(sizeof (struct seg_info), KM_SLEEP);
17247c478bd9Sstevel@tonic-gate 		bank_curr->seg_id = seg_id;
17257c478bd9Sstevel@tonic-gate 		seg_curr->seg_node.id = seg_id++;
17267c478bd9Sstevel@tonic-gate 		seg_curr->base = base;
17277c478bd9Sstevel@tonic-gate 		seg_curr->size = size;
17287c478bd9Sstevel@tonic-gate 		seg_curr->nbanks = 1;
17297c478bd9Sstevel@tonic-gate 		seg_curr->ifactor = ifactor;
17307c478bd9Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)seg_curr, &seg_head, &seg_tail);
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 		nsegments++;
17337c478bd9Sstevel@tonic-gate 	}
17347c478bd9Sstevel@tonic-gate 
17357c478bd9Sstevel@tonic-gate 	/* Get the local id of bank which is only unique per segment. */
17367c478bd9Sstevel@tonic-gate 	bank_curr->local_id = seg_curr->nbanks - 1;
17377c478bd9Sstevel@tonic-gate 
17387c478bd9Sstevel@tonic-gate 	/* add bank at the end of the list; not sorted by bankid */
17397c478bd9Sstevel@tonic-gate 	if (seg_curr->hb_inseg != NULL) {
17407c478bd9Sstevel@tonic-gate 		bank_curr->p_inseg = seg_curr->tb_inseg;
17417c478bd9Sstevel@tonic-gate 		bank_curr->n_inseg = seg_curr->tb_inseg->n_inseg;
17427c478bd9Sstevel@tonic-gate 		seg_curr->tb_inseg->n_inseg = bank_curr;
17437c478bd9Sstevel@tonic-gate 		seg_curr->tb_inseg = bank_curr;
17447c478bd9Sstevel@tonic-gate 	} else {
17457c478bd9Sstevel@tonic-gate 		bank_curr->n_inseg = bank_curr->p_inseg = NULL;
17467c478bd9Sstevel@tonic-gate 		seg_curr->hb_inseg = seg_curr->tb_inseg = bank_curr;
17477c478bd9Sstevel@tonic-gate 	}
17487c478bd9Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: + bank to seg, id %d\n",
17497c478bd9Sstevel@tonic-gate 	    seg_curr->seg_node.id));
17507c478bd9Sstevel@tonic-gate 
17517c478bd9Sstevel@tonic-gate 	mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
17527c478bd9Sstevel@tonic-gate 
17537c478bd9Sstevel@tonic-gate 	memsize += size;
17547c478bd9Sstevel@tonic-gate 	if (seg_curr->nbanks > maxbanks)
17557c478bd9Sstevel@tonic-gate 		maxbanks = seg_curr->nbanks;
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate exit:
17587c478bd9Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
17597c478bd9Sstevel@tonic-gate 	return (status);
17607c478bd9Sstevel@tonic-gate }
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate /*
17637c478bd9Sstevel@tonic-gate  * Delete nodes related to the given MC on mc, device group, device,
17647c478bd9Sstevel@tonic-gate  * and bank lists. Moreover, delete corresponding segment if its connected
17657c478bd9Sstevel@tonic-gate  * banks are all removed.
1766*d00f0155Sayznaga  *
1767*d00f0155Sayznaga  * The "delete" argument is 1 if this is called as a result of DDI_DETACH. In
1768*d00f0155Sayznaga  * this case, the DIMM data structures need to be deleted. The argument is
1769*d00f0155Sayznaga  * 0 if this called as a result of DDI_SUSPEND/DDI_RESUME. In this case,
1770*d00f0155Sayznaga  * the DIMM data structures are left alone.
17717c478bd9Sstevel@tonic-gate  */
17727c478bd9Sstevel@tonic-gate static void
1773*d00f0155Sayznaga mlayout_del(int mc_id, int delete)
17747c478bd9Sstevel@tonic-gate {
17757c478bd9Sstevel@tonic-gate 	int i, j, dgrpid, devid, bankid, ndevgrps;
17767c478bd9Sstevel@tonic-gate 	struct seg_info *seg;
17777c478bd9Sstevel@tonic-gate 	struct bank_info *bank_curr;
17787c478bd9Sstevel@tonic-gate 	struct mctrl_info *mctrl;
17797c478bd9Sstevel@tonic-gate 	mc_dlist_t *dgrp_ptr;
17807c478bd9Sstevel@tonic-gate 	mc_dlist_t *dev_ptr;
17817c478bd9Sstevel@tonic-gate 	uint64_t base;
17827c478bd9Sstevel@tonic-gate 
17837c478bd9Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
17847c478bd9Sstevel@tonic-gate 
17857c478bd9Sstevel@tonic-gate 	/* delete mctrl_info */
17867c478bd9Sstevel@tonic-gate 	if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id, mctrl_head)) !=
17877c478bd9Sstevel@tonic-gate 	    NULL) {
17887c478bd9Sstevel@tonic-gate 		ndevgrps = mctrl->ndevgrps;
17897c478bd9Sstevel@tonic-gate 		mc_node_del((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
17907c478bd9Sstevel@tonic-gate 		kmem_free(mctrl, sizeof (struct mctrl_info));
17917c478bd9Sstevel@tonic-gate 		nmcs--;
17927c478bd9Sstevel@tonic-gate 
17937c478bd9Sstevel@tonic-gate 		/*
17947c478bd9Sstevel@tonic-gate 		 * There is no other list left for disabled MC.
17957c478bd9Sstevel@tonic-gate 		 */
17967c478bd9Sstevel@tonic-gate 		if (ndevgrps == 0) {
17977c478bd9Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
17987c478bd9Sstevel@tonic-gate 			return;
17997c478bd9Sstevel@tonic-gate 		}
18007c478bd9Sstevel@tonic-gate 	} else
18017c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "MC mlayout_del: mctrl is not found\n");
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 	/* Delete device groups and devices of the detached MC */
18047c478bd9Sstevel@tonic-gate 	for (i = 0; i < NDGRPS; i++) {
18057c478bd9Sstevel@tonic-gate 		dgrpid = mc_id * NDGRPS + i;
18067c478bd9Sstevel@tonic-gate 		if (!(dgrp_ptr = mc_node_get(dgrpid, dgrp_head))) {
18077c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_del: no devgrp %d\n", dgrpid);
18087c478bd9Sstevel@tonic-gate 			continue;
18097c478bd9Sstevel@tonic-gate 		}
18107c478bd9Sstevel@tonic-gate 
18117c478bd9Sstevel@tonic-gate 		for (j = 0; j < NDIMMS; j++) {
18127c478bd9Sstevel@tonic-gate 			devid = dgrpid * NDIMMS + j;
18137c478bd9Sstevel@tonic-gate 			if (dev_ptr = mc_node_get(devid, device_head)) {
18147c478bd9Sstevel@tonic-gate 				mc_node_del(dev_ptr, &device_head,
18157c478bd9Sstevel@tonic-gate 				    &device_tail);
18167c478bd9Sstevel@tonic-gate 				kmem_free(dev_ptr, sizeof (struct device_info));
18177c478bd9Sstevel@tonic-gate 			} else {
18187c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "mlayout_del: no dev %d\n",
18197c478bd9Sstevel@tonic-gate 				    devid);
18207c478bd9Sstevel@tonic-gate 			}
18217c478bd9Sstevel@tonic-gate 		}
18227c478bd9Sstevel@tonic-gate 
18237c478bd9Sstevel@tonic-gate 		mc_node_del(dgrp_ptr, &dgrp_head, &dgrp_tail);
18247c478bd9Sstevel@tonic-gate 		kmem_free(dgrp_ptr, sizeof (struct dgrp_info));
18257c478bd9Sstevel@tonic-gate 	}
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate 	/* Delete banks and segments if it has no bank */
18287c478bd9Sstevel@tonic-gate 	for (i = 0; i < NBANKS; i++) {
18297c478bd9Sstevel@tonic-gate 		bankid = mc_id * NBANKS + i;
18307c478bd9Sstevel@tonic-gate 		DPRINTF(MC_DESTRC_DEBUG, ("bank id %d\n", bankid));
18317c478bd9Sstevel@tonic-gate 		if (!(bank_curr = (struct bank_info *)mc_node_get(bankid,
18327c478bd9Sstevel@tonic-gate 		    bank_head))) {
18337c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_del: no bank %d\n", bankid);
18347c478bd9Sstevel@tonic-gate 			continue;
18357c478bd9Sstevel@tonic-gate 		}
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate 		if (bank_curr->valid) {
18387c478bd9Sstevel@tonic-gate 			base = bank_curr->um & ~bank_curr->uk;
18397c478bd9Sstevel@tonic-gate 			base <<= MADR_UPA_SHIFT;
18407c478bd9Sstevel@tonic-gate 			bank_curr->valid = 0;
18417c478bd9Sstevel@tonic-gate 			memsize -= bank_curr->size;
18427c478bd9Sstevel@tonic-gate 
18437c478bd9Sstevel@tonic-gate 			/* Delete bank at segment and segment if no bank left */
18447c478bd9Sstevel@tonic-gate 			if (!(seg = seg_match_base(base))) {
18457c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "mlayout_del: no seg\n");
18467c478bd9Sstevel@tonic-gate 				mc_node_del((mc_dlist_t *)bank_curr, &bank_head,
18477c478bd9Sstevel@tonic-gate 				    &bank_tail);
18487c478bd9Sstevel@tonic-gate 				kmem_free(bank_curr, sizeof (struct bank_info));
18497c478bd9Sstevel@tonic-gate 				continue;
18507c478bd9Sstevel@tonic-gate 			}
18517c478bd9Sstevel@tonic-gate 
18527c478bd9Sstevel@tonic-gate 			/* update the bank list at the segment */
18537c478bd9Sstevel@tonic-gate 			if (bank_curr->n_inseg == NULL) {
18547c478bd9Sstevel@tonic-gate 				/* node is at the tail of list */
18557c478bd9Sstevel@tonic-gate 				seg->tb_inseg = bank_curr->p_inseg;
18567c478bd9Sstevel@tonic-gate 			} else {
18577c478bd9Sstevel@tonic-gate 				bank_curr->n_inseg->p_inseg =
18587c478bd9Sstevel@tonic-gate 				    bank_curr->p_inseg;
18597c478bd9Sstevel@tonic-gate 			}
18607c478bd9Sstevel@tonic-gate 
18617c478bd9Sstevel@tonic-gate 			if (bank_curr->p_inseg == NULL) {
18627c478bd9Sstevel@tonic-gate 				/* node is at the head of list */
18637c478bd9Sstevel@tonic-gate 				seg->hb_inseg = bank_curr->n_inseg;
18647c478bd9Sstevel@tonic-gate 			} else {
18657c478bd9Sstevel@tonic-gate 				bank_curr->p_inseg->n_inseg =
18667c478bd9Sstevel@tonic-gate 				    bank_curr->n_inseg;
18677c478bd9Sstevel@tonic-gate 			}
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 			seg->nbanks--;
18707c478bd9Sstevel@tonic-gate 			seg->size -= bank_curr->size;
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate 			if (seg->nbanks == 0) {
18737c478bd9Sstevel@tonic-gate 				mc_node_del((mc_dlist_t *)seg, &seg_head,
18747c478bd9Sstevel@tonic-gate 				    &seg_tail);
18757c478bd9Sstevel@tonic-gate 				kmem_free(seg, sizeof (struct seg_info));
18767c478bd9Sstevel@tonic-gate 				nsegments--;
18777c478bd9Sstevel@tonic-gate 			}
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate 		}
18807c478bd9Sstevel@tonic-gate 		mc_node_del((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
18817c478bd9Sstevel@tonic-gate 		kmem_free(bank_curr, sizeof (struct bank_info));
18827c478bd9Sstevel@tonic-gate 	}	/* end of for loop for four banks */
18837c478bd9Sstevel@tonic-gate 
1884*d00f0155Sayznaga 	if (mc_dimm_sids && delete) {
1885*d00f0155Sayznaga 		rw_enter(&mcdimmsids_rw, RW_WRITER);
1886*d00f0155Sayznaga 		i = mc_get_sid_cache_index(mc_id);
1887*d00f0155Sayznaga 		if (i >= 0) {
1888*d00f0155Sayznaga 			mc_dimm_sids[i].state = MC_DIMM_SIDS_INVALID;
1889*d00f0155Sayznaga 			if (mc_dimm_sids[i].sids) {
1890*d00f0155Sayznaga 				kmem_free(mc_dimm_sids[i].sids,
1891*d00f0155Sayznaga 				    sizeof (dimm_sid_t) * (NDGRPS * NDIMMS));
1892*d00f0155Sayznaga 				mc_dimm_sids[i].sids = NULL;
1893*d00f0155Sayznaga 			}
1894*d00f0155Sayznaga 		}
1895*d00f0155Sayznaga 		rw_exit(&mcdimmsids_rw);
1896*d00f0155Sayznaga 	}
1897*d00f0155Sayznaga 
18987c478bd9Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
18997c478bd9Sstevel@tonic-gate }
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate /*
19027c478bd9Sstevel@tonic-gate  * Search the segment in the list starting at seg_head by base address
19037c478bd9Sstevel@tonic-gate  * input: base address
19047c478bd9Sstevel@tonic-gate  * return: pointer of found segment or null if not found.
19057c478bd9Sstevel@tonic-gate  */
19067c478bd9Sstevel@tonic-gate static struct seg_info *
19077c478bd9Sstevel@tonic-gate seg_match_base(u_longlong_t base)
19087c478bd9Sstevel@tonic-gate {
19097c478bd9Sstevel@tonic-gate 	static struct seg_info *seg_ptr;
19107c478bd9Sstevel@tonic-gate 
19117c478bd9Sstevel@tonic-gate 	seg_ptr = (struct seg_info *)seg_head;
19127c478bd9Sstevel@tonic-gate 	while (seg_ptr != NULL) {
1913f47a9c50Smathue 		DPRINTF(MC_LIST_DEBUG, ("seg_match: base %lu,given base %llu\n",
19147c478bd9Sstevel@tonic-gate 		    seg_ptr->base, base));
19157c478bd9Sstevel@tonic-gate 		if (seg_ptr->base == base)
19167c478bd9Sstevel@tonic-gate 			break;
19177c478bd9Sstevel@tonic-gate 		seg_ptr = (struct seg_info *)seg_ptr->seg_node.next;
19187c478bd9Sstevel@tonic-gate 	}
19197c478bd9Sstevel@tonic-gate 	return (seg_ptr);
19207c478bd9Sstevel@tonic-gate }
19217c478bd9Sstevel@tonic-gate 
19227c478bd9Sstevel@tonic-gate /*
19237c478bd9Sstevel@tonic-gate  * mc_dlist is a double linking list, including unique id, and pointers to
19247c478bd9Sstevel@tonic-gate  * next, and previous nodes. seg_info, bank_info, dgrp_info, device_info,
19257c478bd9Sstevel@tonic-gate  * and mctrl_info has it at the top to share the operations, add, del, and get.
19267c478bd9Sstevel@tonic-gate  *
19277c478bd9Sstevel@tonic-gate  * The new node is added at the tail and is not sorted.
19287c478bd9Sstevel@tonic-gate  *
19297c478bd9Sstevel@tonic-gate  * Input: The pointer of node to be added, head and tail of the list
19307c478bd9Sstevel@tonic-gate  */
19317c478bd9Sstevel@tonic-gate 
19327c478bd9Sstevel@tonic-gate static void
19337c478bd9Sstevel@tonic-gate mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
19347c478bd9Sstevel@tonic-gate {
19357c478bd9Sstevel@tonic-gate 	DPRINTF(MC_LIST_DEBUG, ("mc_node_add: node->id %d head %p tail %p\n",
19367c478bd9Sstevel@tonic-gate 		node->id, *head, *tail));
19377c478bd9Sstevel@tonic-gate 
19387c478bd9Sstevel@tonic-gate 	if (*head != NULL) {
19397c478bd9Sstevel@tonic-gate 		node->prev = *tail;
19407c478bd9Sstevel@tonic-gate 		node->next = (*tail)->next;
19417c478bd9Sstevel@tonic-gate 		(*tail)->next = node;
19427c478bd9Sstevel@tonic-gate 		*tail = node;
19437c478bd9Sstevel@tonic-gate 	} else {
19447c478bd9Sstevel@tonic-gate 		node->next = node->prev = NULL;
19457c478bd9Sstevel@tonic-gate 		*head = *tail = node;
19467c478bd9Sstevel@tonic-gate 	}
19477c478bd9Sstevel@tonic-gate }
19487c478bd9Sstevel@tonic-gate 
19497c478bd9Sstevel@tonic-gate /*
19507c478bd9Sstevel@tonic-gate  * Input: The pointer of node to be deleted, head and tail of the list
19517c478bd9Sstevel@tonic-gate  *
19527c478bd9Sstevel@tonic-gate  * Deleted node will be at the following positions
19537c478bd9Sstevel@tonic-gate  * 1. At the tail of the list
19547c478bd9Sstevel@tonic-gate  * 2. At the head of the list
19557c478bd9Sstevel@tonic-gate  * 3. At the head and tail of the list, i.e. only one left.
19567c478bd9Sstevel@tonic-gate  * 4. At the middle of the list
19577c478bd9Sstevel@tonic-gate  */
19587c478bd9Sstevel@tonic-gate 
19597c478bd9Sstevel@tonic-gate static void
19607c478bd9Sstevel@tonic-gate mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
19617c478bd9Sstevel@tonic-gate {
19627c478bd9Sstevel@tonic-gate 	if (node->next == NULL) {
19637c478bd9Sstevel@tonic-gate 		/* deleted node is at the tail of list */
19647c478bd9Sstevel@tonic-gate 		*tail = node->prev;
19657c478bd9Sstevel@tonic-gate 	} else {
19667c478bd9Sstevel@tonic-gate 		node->next->prev = node->prev;
19677c478bd9Sstevel@tonic-gate 	}
19687c478bd9Sstevel@tonic-gate 
19697c478bd9Sstevel@tonic-gate 	if (node->prev == NULL) {
19707c478bd9Sstevel@tonic-gate 		/* deleted node is at the head of list */
19717c478bd9Sstevel@tonic-gate 		*head = node->next;
19727c478bd9Sstevel@tonic-gate 	} else {
19737c478bd9Sstevel@tonic-gate 		node->prev->next = node->next;
19747c478bd9Sstevel@tonic-gate 	}
19757c478bd9Sstevel@tonic-gate }
19767c478bd9Sstevel@tonic-gate 
19777c478bd9Sstevel@tonic-gate /*
19787c478bd9Sstevel@tonic-gate  * Search the list from the head of the list to match the given id
19797c478bd9Sstevel@tonic-gate  * Input: id and the head of the list
19807c478bd9Sstevel@tonic-gate  * Return: pointer of found node
19817c478bd9Sstevel@tonic-gate  */
19827c478bd9Sstevel@tonic-gate static mc_dlist_t *
19837c478bd9Sstevel@tonic-gate mc_node_get(int id, mc_dlist_t *head)
19847c478bd9Sstevel@tonic-gate {
19857c478bd9Sstevel@tonic-gate 	mc_dlist_t *node;
19867c478bd9Sstevel@tonic-gate 
19877c478bd9Sstevel@tonic-gate 	node = head;
19887c478bd9Sstevel@tonic-gate 	while (node != NULL) {
19897c478bd9Sstevel@tonic-gate 		DPRINTF(MC_LIST_DEBUG, ("mc_node_get: id %d, given id %d\n",
19907c478bd9Sstevel@tonic-gate 		    node->id, id));
19917c478bd9Sstevel@tonic-gate 		if (node->id == id)
19927c478bd9Sstevel@tonic-gate 			break;
19937c478bd9Sstevel@tonic-gate 		node = node->next;
19947c478bd9Sstevel@tonic-gate 	}
19957c478bd9Sstevel@tonic-gate 	return (node);
19967c478bd9Sstevel@tonic-gate }
19977c478bd9Sstevel@tonic-gate 
19987c478bd9Sstevel@tonic-gate /*
19997c478bd9Sstevel@tonic-gate  * mc-us3 driver allows a platform to add extra label
20007c478bd9Sstevel@tonic-gate  * information to the unum string. If a platform implements a
20017c478bd9Sstevel@tonic-gate  * kernel function called plat_add_mem_unum_label() it will be
20027c478bd9Sstevel@tonic-gate  * executed. This would typically be implemented in the platmod.
20037c478bd9Sstevel@tonic-gate  */
20047c478bd9Sstevel@tonic-gate static void
20057c478bd9Sstevel@tonic-gate mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm)
20067c478bd9Sstevel@tonic-gate {
20077c478bd9Sstevel@tonic-gate 	if (&plat_add_mem_unum_label)
20087c478bd9Sstevel@tonic-gate 		plat_add_mem_unum_label(buf, mcid, bank, dimm);
20097c478bd9Sstevel@tonic-gate }
2010*d00f0155Sayznaga 
2011*d00f0155Sayznaga static int
2012*d00f0155Sayznaga mc_get_sid_cache_index(int mcid)
2013*d00f0155Sayznaga {
2014*d00f0155Sayznaga 	int	i;
2015*d00f0155Sayznaga 
2016*d00f0155Sayznaga 	for (i = 0; i < max_entries; i++) {
2017*d00f0155Sayznaga 		if (mcid == mc_dimm_sids[i].mcid)
2018*d00f0155Sayznaga 			return (i);
2019*d00f0155Sayznaga 	}
2020*d00f0155Sayznaga 
2021*d00f0155Sayznaga 	return (-1);
2022*d00f0155Sayznaga }
2023*d00f0155Sayznaga 
2024*d00f0155Sayznaga static int
2025*d00f0155Sayznaga mc_populate_sid_cache(void)
2026*d00f0155Sayznaga {
2027*d00f0155Sayznaga 	struct bank_info	*bank;
2028*d00f0155Sayznaga 
2029*d00f0155Sayznaga 	if (&plat_populate_sid_cache == 0)
2030*d00f0155Sayznaga 		return (ENOTSUP);
2031*d00f0155Sayznaga 
2032*d00f0155Sayznaga 	ASSERT(RW_WRITE_HELD(&mcdimmsids_rw));
2033*d00f0155Sayznaga 
2034*d00f0155Sayznaga 	/*
2035*d00f0155Sayznaga 	 * Mark which MCs are present and which segment
2036*d00f0155Sayznaga 	 * the DIMMs belong to.  Allocate space to
2037*d00f0155Sayznaga 	 * store DIMM serial ids which are later provided
2038*d00f0155Sayznaga 	 * by the platform layer, and update each bank_info
2039*d00f0155Sayznaga 	 * structure with pointers to its serial ids.
2040*d00f0155Sayznaga 	 */
2041*d00f0155Sayznaga 	bank = (struct bank_info *)bank_head;
2042*d00f0155Sayznaga 	while (bank != NULL) {
2043*d00f0155Sayznaga 		int i, j;
2044*d00f0155Sayznaga 		int bankid, mcid, dgrp_no;
2045*d00f0155Sayznaga 
2046*d00f0155Sayznaga 		if (!bank->valid) {
2047*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
2048*d00f0155Sayznaga 			continue;
2049*d00f0155Sayznaga 		}
2050*d00f0155Sayznaga 
2051*d00f0155Sayznaga 		bankid = bank->bank_node.id;
2052*d00f0155Sayznaga 		mcid = bankid / NBANKS;
2053*d00f0155Sayznaga 		i = mc_get_sid_cache_index(mcid);
2054*d00f0155Sayznaga 		if (mc_dimm_sids[i].state == MC_DIMM_SIDS_AVAILABLE) {
2055*d00f0155Sayznaga 			bank = (struct bank_info *)bank->bank_node.next;
2056*d00f0155Sayznaga 			continue;
2057*d00f0155Sayznaga 		} else if (mc_dimm_sids[i].state != MC_DIMM_SIDS_REQUESTED) {
2058*d00f0155Sayznaga 			mc_dimm_sids[i].state = MC_DIMM_SIDS_REQUESTED;
2059*d00f0155Sayznaga 			mc_dimm_sids[i].seg_id = bank->seg_id;
2060*d00f0155Sayznaga 		}
2061*d00f0155Sayznaga 
2062*d00f0155Sayznaga 		if (mc_dimm_sids[i].sids == NULL) {
2063*d00f0155Sayznaga 			mc_dimm_sids[i].sids = (dimm_sid_t *)kmem_zalloc(
2064*d00f0155Sayznaga 			    sizeof (dimm_sid_t) * (NDGRPS * NDIMMS), KM_SLEEP);
2065*d00f0155Sayznaga 		}
2066*d00f0155Sayznaga 
2067*d00f0155Sayznaga 		dgrp_no = bank->devgrp_id % NDGRPS;
2068*d00f0155Sayznaga 
2069*d00f0155Sayznaga 		for (j = 0; j < NDIMMS; j++)
2070*d00f0155Sayznaga 			bank->dimmsidp[j] =
2071*d00f0155Sayznaga 			    &mc_dimm_sids[i].sids[j + (NDIMMS * dgrp_no)];
2072*d00f0155Sayznaga 
2073*d00f0155Sayznaga 		bank = (struct bank_info *)bank->bank_node.next;
2074*d00f0155Sayznaga 	}
2075*d00f0155Sayznaga 
2076*d00f0155Sayznaga 
2077*d00f0155Sayznaga 	/*
2078*d00f0155Sayznaga 	 * Call to the platform layer to populate the cache
2079*d00f0155Sayznaga 	 * with DIMM serial ids.
2080*d00f0155Sayznaga 	 */
2081*d00f0155Sayznaga 	return (plat_populate_sid_cache(mc_dimm_sids, max_entries));
2082*d00f0155Sayznaga }
2083*d00f0155Sayznaga 
2084*d00f0155Sayznaga static void
2085*d00f0155Sayznaga mc_init_sid_cache_thr(void)
2086*d00f0155Sayznaga {
2087*d00f0155Sayznaga 	ASSERT(mc_dimm_sids == NULL);
2088*d00f0155Sayznaga 
2089*d00f0155Sayznaga 	mutex_enter(&mcdatamutex);
2090*d00f0155Sayznaga 	rw_enter(&mcdimmsids_rw, RW_WRITER);
2091*d00f0155Sayznaga 
2092*d00f0155Sayznaga 	mc_dimm_sids = plat_alloc_sid_cache(&max_entries);
2093*d00f0155Sayznaga 	(void) mc_populate_sid_cache();
2094*d00f0155Sayznaga 
2095*d00f0155Sayznaga 	rw_exit(&mcdimmsids_rw);
2096*d00f0155Sayznaga 	mutex_exit(&mcdatamutex);
2097*d00f0155Sayznaga }
2098*d00f0155Sayznaga 
2099*d00f0155Sayznaga static int
2100*d00f0155Sayznaga mc_init_sid_cache(void)
2101*d00f0155Sayznaga {
2102*d00f0155Sayznaga 	if (&plat_alloc_sid_cache) {
2103*d00f0155Sayznaga 		(void) thread_create(NULL, 0, mc_init_sid_cache_thr, NULL, 0,
2104*d00f0155Sayznaga 		    &p0, TS_RUN, minclsyspri);
2105*d00f0155Sayznaga 		return (0);
2106*d00f0155Sayznaga 	} else
2107*d00f0155Sayznaga 		return (ENOTSUP);
2108*d00f0155Sayznaga }
2109*d00f0155Sayznaga 
2110*d00f0155Sayznaga static int
2111*d00f0155Sayznaga mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp)
2112*d00f0155Sayznaga {
2113*d00f0155Sayznaga 	int	i;
2114*d00f0155Sayznaga 
2115*d00f0155Sayznaga 	if (buflen < DIMM_SERIAL_ID_LEN)
2116*d00f0155Sayznaga 		return (ENOSPC);
2117*d00f0155Sayznaga 
2118*d00f0155Sayznaga 	/*
2119*d00f0155Sayznaga 	 * If DIMM serial ids have not been cached yet, tell the
2120*d00f0155Sayznaga 	 * caller to try again.
2121*d00f0155Sayznaga 	 */
2122*d00f0155Sayznaga 	if (!rw_tryenter(&mcdimmsids_rw, RW_READER))
2123*d00f0155Sayznaga 		return (EAGAIN);
2124*d00f0155Sayznaga 
2125*d00f0155Sayznaga 	if (mc_dimm_sids == NULL) {
2126*d00f0155Sayznaga 		rw_exit(&mcdimmsids_rw);
2127*d00f0155Sayznaga 		return (EAGAIN);
2128*d00f0155Sayznaga 	}
2129*d00f0155Sayznaga 
2130*d00f0155Sayznaga 	/*
2131*d00f0155Sayznaga 	 * Find dimm serial id using mcid and dimm #
2132*d00f0155Sayznaga 	 */
2133*d00f0155Sayznaga 	for (i = 0; i < max_entries; i++) {
2134*d00f0155Sayznaga 		if (mc_dimm_sids[i].mcid == mcid)
2135*d00f0155Sayznaga 			break;
2136*d00f0155Sayznaga 	}
2137*d00f0155Sayznaga 	if ((i == max_entries) || (!mc_dimm_sids[i].sids)) {
2138*d00f0155Sayznaga 		rw_exit(&mcdimmsids_rw);
2139*d00f0155Sayznaga 		return (ENOENT);
2140*d00f0155Sayznaga 	}
2141*d00f0155Sayznaga 
2142*d00f0155Sayznaga 	(void) strlcpy(buf, mc_dimm_sids[i].sids[dimm],
2143*d00f0155Sayznaga 	    DIMM_SERIAL_ID_LEN);
2144*d00f0155Sayznaga 	*lenp = strlen(buf);
2145*d00f0155Sayznaga 
2146*d00f0155Sayznaga 	rw_exit(&mcdimmsids_rw);
2147*d00f0155Sayznaga 	return (0);
2148*d00f0155Sayznaga }
2149