xref: /onnv-gate/usr/src/uts/sun4u/io/mc-us3.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/conf.h>
31*0Sstevel@tonic-gate #include <sys/ddi.h>
32*0Sstevel@tonic-gate #include <sys/stat.h>
33*0Sstevel@tonic-gate #include <sys/sunddi.h>
34*0Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
35*0Sstevel@tonic-gate #include <sys/obpdefs.h>
36*0Sstevel@tonic-gate #include <sys/cmn_err.h>
37*0Sstevel@tonic-gate #include <sys/errno.h>
38*0Sstevel@tonic-gate #include <sys/kmem.h>
39*0Sstevel@tonic-gate #include <sys/open.h>
40*0Sstevel@tonic-gate #include <sys/thread.h>
41*0Sstevel@tonic-gate #include <sys/cpuvar.h>
42*0Sstevel@tonic-gate #include <sys/x_call.h>
43*0Sstevel@tonic-gate #include <sys/debug.h>
44*0Sstevel@tonic-gate #include <sys/sysmacros.h>
45*0Sstevel@tonic-gate #include <sys/ivintr.h>
46*0Sstevel@tonic-gate #include <sys/intr.h>
47*0Sstevel@tonic-gate #include <sys/intreg.h>
48*0Sstevel@tonic-gate #include <sys/autoconf.h>
49*0Sstevel@tonic-gate #include <sys/modctl.h>
50*0Sstevel@tonic-gate #include <sys/spl.h>
51*0Sstevel@tonic-gate #include <sys/async.h>
52*0Sstevel@tonic-gate #include <sys/mc.h>
53*0Sstevel@tonic-gate #include <sys/mc-us3.h>
54*0Sstevel@tonic-gate #include <sys/cpu_module.h>
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate /*
57*0Sstevel@tonic-gate  * Function prototypes
58*0Sstevel@tonic-gate  */
59*0Sstevel@tonic-gate 
60*0Sstevel@tonic-gate static int mc_open(dev_t *, int, int, cred_t *);
61*0Sstevel@tonic-gate static int mc_close(dev_t, int, int, cred_t *);
62*0Sstevel@tonic-gate static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
63*0Sstevel@tonic-gate static int mc_attach(dev_info_t *, ddi_attach_cmd_t);
64*0Sstevel@tonic-gate static int mc_detach(dev_info_t *, ddi_detach_cmd_t);
65*0Sstevel@tonic-gate 
66*0Sstevel@tonic-gate /*
67*0Sstevel@tonic-gate  * Configuration data structures
68*0Sstevel@tonic-gate  */
69*0Sstevel@tonic-gate static struct cb_ops mc_cb_ops = {
70*0Sstevel@tonic-gate 	mc_open,			/* open */
71*0Sstevel@tonic-gate 	mc_close,			/* close */
72*0Sstevel@tonic-gate 	nulldev,			/* strategy */
73*0Sstevel@tonic-gate 	nulldev,			/* print */
74*0Sstevel@tonic-gate 	nodev,				/* dump */
75*0Sstevel@tonic-gate 	nulldev,			/* read */
76*0Sstevel@tonic-gate 	nulldev,			/* write */
77*0Sstevel@tonic-gate 	mc_ioctl,			/* ioctl */
78*0Sstevel@tonic-gate 	nodev,				/* devmap */
79*0Sstevel@tonic-gate 	nodev,				/* mmap */
80*0Sstevel@tonic-gate 	nodev,				/* segmap */
81*0Sstevel@tonic-gate 	nochpoll,			/* poll */
82*0Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
83*0Sstevel@tonic-gate 	0,				/* streamtab */
84*0Sstevel@tonic-gate 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
85*0Sstevel@tonic-gate 	CB_REV,				/* rev */
86*0Sstevel@tonic-gate 	nodev,				/* cb_aread */
87*0Sstevel@tonic-gate 	nodev				/* cb_awrite */
88*0Sstevel@tonic-gate };
89*0Sstevel@tonic-gate 
90*0Sstevel@tonic-gate static struct dev_ops mc_ops = {
91*0Sstevel@tonic-gate 	DEVO_REV,			/* rev */
92*0Sstevel@tonic-gate 	0,				/* refcnt  */
93*0Sstevel@tonic-gate 	ddi_getinfo_1to1,		/* getinfo */
94*0Sstevel@tonic-gate 	nulldev,			/* identify */
95*0Sstevel@tonic-gate 	nulldev,			/* probe */
96*0Sstevel@tonic-gate 	mc_attach,			/* attach */
97*0Sstevel@tonic-gate 	mc_detach,			/* detach */
98*0Sstevel@tonic-gate 	nulldev,			/* reset */
99*0Sstevel@tonic-gate 	&mc_cb_ops,			/* cb_ops */
100*0Sstevel@tonic-gate 	(struct bus_ops *)0,		/* bus_ops */
101*0Sstevel@tonic-gate 	nulldev				/* power */
102*0Sstevel@tonic-gate };
103*0Sstevel@tonic-gate 
104*0Sstevel@tonic-gate /*
105*0Sstevel@tonic-gate  * Driver globals
106*0Sstevel@tonic-gate  */
107*0Sstevel@tonic-gate static void *mcp;
108*0Sstevel@tonic-gate static int nmcs = 0;
109*0Sstevel@tonic-gate static int seg_id = 0;
110*0Sstevel@tonic-gate static int nsegments = 0;
111*0Sstevel@tonic-gate static uint64_t memsize = 0;
112*0Sstevel@tonic-gate static int maxbanks = 0;
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate static mc_dlist_t *seg_head, *seg_tail, *bank_head, *bank_tail;
115*0Sstevel@tonic-gate static mc_dlist_t *mctrl_head, *mctrl_tail, *dgrp_head, *dgrp_tail;
116*0Sstevel@tonic-gate static mc_dlist_t *device_head, *device_tail;
117*0Sstevel@tonic-gate 
118*0Sstevel@tonic-gate static kmutex_t	mcmutex;
119*0Sstevel@tonic-gate static kmutex_t	mcdatamutex;
120*0Sstevel@tonic-gate static int mc_is_open = 0;
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate extern struct mod_ops mod_driverops;
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate static struct modldrv modldrv = {
125*0Sstevel@tonic-gate 	&mod_driverops,			/* module type, this one is a driver */
126*0Sstevel@tonic-gate 	"Memory-controller: %I%",	/* module name */
127*0Sstevel@tonic-gate 	&mc_ops,			/* driver ops */
128*0Sstevel@tonic-gate };
129*0Sstevel@tonic-gate 
130*0Sstevel@tonic-gate static struct modlinkage modlinkage = {
131*0Sstevel@tonic-gate 	MODREV_1,		/* rev */
132*0Sstevel@tonic-gate 	(void *)&modldrv,
133*0Sstevel@tonic-gate 	NULL
134*0Sstevel@tonic-gate };
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate static int mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf,
137*0Sstevel@tonic-gate     int buflen, int *lenp);
138*0Sstevel@tonic-gate static int mc_get_mem_info(int synd_code, uint64_t paddr,
139*0Sstevel@tonic-gate     uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
140*0Sstevel@tonic-gate     int *segsp, int *banksp, int *mcidp);
141*0Sstevel@tonic-gate static int mc_get_mcregs(struct mc_soft_state *);
142*0Sstevel@tonic-gate static void mc_construct(int mc_id, void *dimminfop);
143*0Sstevel@tonic-gate static int mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop);
144*0Sstevel@tonic-gate static void mlayout_del(int mc_id);
145*0Sstevel@tonic-gate static struct seg_info *seg_match_base(u_longlong_t base);
146*0Sstevel@tonic-gate static void mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
147*0Sstevel@tonic-gate static void mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail);
148*0Sstevel@tonic-gate static mc_dlist_t *mc_node_get(int id, mc_dlist_t *head);
149*0Sstevel@tonic-gate static void mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm);
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate #pragma weak p2get_mem_unum
152*0Sstevel@tonic-gate #pragma weak p2get_mem_info
153*0Sstevel@tonic-gate #pragma weak plat_add_mem_unum_label
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate /*
156*0Sstevel@tonic-gate  * These are the module initialization routines.
157*0Sstevel@tonic-gate  */
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate int
160*0Sstevel@tonic-gate _init(void)
161*0Sstevel@tonic-gate {
162*0Sstevel@tonic-gate 	int error;
163*0Sstevel@tonic-gate 
164*0Sstevel@tonic-gate 	if ((error = ddi_soft_state_init(&mcp,
165*0Sstevel@tonic-gate 	    sizeof (struct mc_soft_state), 1)) != 0)
166*0Sstevel@tonic-gate 		return (error);
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 	error =  mod_install(&modlinkage);
169*0Sstevel@tonic-gate 	if (error == 0) {
170*0Sstevel@tonic-gate 		mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL);
171*0Sstevel@tonic-gate 		mutex_init(&mcdatamutex, NULL, MUTEX_DRIVER, NULL);
172*0Sstevel@tonic-gate 	}
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate 	return (error);
175*0Sstevel@tonic-gate }
176*0Sstevel@tonic-gate 
177*0Sstevel@tonic-gate int
178*0Sstevel@tonic-gate _fini(void)
179*0Sstevel@tonic-gate {
180*0Sstevel@tonic-gate 	int error;
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate 	if ((error = mod_remove(&modlinkage)) != 0)
183*0Sstevel@tonic-gate 		return (error);
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate 	ddi_soft_state_fini(&mcp);
186*0Sstevel@tonic-gate 	mutex_destroy(&mcmutex);
187*0Sstevel@tonic-gate 	mutex_destroy(&mcdatamutex);
188*0Sstevel@tonic-gate 
189*0Sstevel@tonic-gate 	return (0);
190*0Sstevel@tonic-gate }
191*0Sstevel@tonic-gate 
192*0Sstevel@tonic-gate int
193*0Sstevel@tonic-gate _info(struct modinfo *modinfop)
194*0Sstevel@tonic-gate {
195*0Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
196*0Sstevel@tonic-gate }
197*0Sstevel@tonic-gate 
198*0Sstevel@tonic-gate static int
199*0Sstevel@tonic-gate mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
200*0Sstevel@tonic-gate {
201*0Sstevel@tonic-gate 	struct mc_soft_state *softsp;
202*0Sstevel@tonic-gate 	struct dimm_info *dimminfop;
203*0Sstevel@tonic-gate 	int instance, len, err;
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 	/* get the instance of this devi */
206*0Sstevel@tonic-gate 	instance = ddi_get_instance(devi);
207*0Sstevel@tonic-gate 
208*0Sstevel@tonic-gate 	switch (cmd) {
209*0Sstevel@tonic-gate 	case DDI_ATTACH:
210*0Sstevel@tonic-gate 		break;
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate 	case DDI_RESUME:
213*0Sstevel@tonic-gate 		/* get the soft state pointer for this device node */
214*0Sstevel@tonic-gate 		softsp = ddi_get_soft_state(mcp, instance);
215*0Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: DDI_RESUME: updating MADRs\n",
216*0Sstevel@tonic-gate 		    instance));
217*0Sstevel@tonic-gate 		/*
218*0Sstevel@tonic-gate 		 * During resume, the source and target board's bank_infos
219*0Sstevel@tonic-gate 		 * need to be updated with the new mc MADR values.  This is
220*0Sstevel@tonic-gate 		 * implemented with existing functionality by first removing
221*0Sstevel@tonic-gate 		 * the props and allocated data structs, and then adding them
222*0Sstevel@tonic-gate 		 * back in.
223*0Sstevel@tonic-gate 		 */
224*0Sstevel@tonic-gate 		if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
225*0Sstevel@tonic-gate 		    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
226*0Sstevel@tonic-gate 		    MEM_CFG_PROP_NAME) == 1) {
227*0Sstevel@tonic-gate 			(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
228*0Sstevel@tonic-gate 			    MEM_CFG_PROP_NAME);
229*0Sstevel@tonic-gate 		}
230*0Sstevel@tonic-gate 		mlayout_del(softsp->portid);
231*0Sstevel@tonic-gate 		if (mc_get_mcregs(softsp) == -1) {
232*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "mc_attach: mc%d DDI_RESUME failure\n",
233*0Sstevel@tonic-gate 			    instance);
234*0Sstevel@tonic-gate 		}
235*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
236*0Sstevel@tonic-gate 
237*0Sstevel@tonic-gate 	default:
238*0Sstevel@tonic-gate 		return (DDI_FAILURE);
239*0Sstevel@tonic-gate 	}
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(mcp, instance) != DDI_SUCCESS)
242*0Sstevel@tonic-gate 		return (DDI_FAILURE);
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 	softsp = ddi_get_soft_state(mcp, instance);
245*0Sstevel@tonic-gate 
246*0Sstevel@tonic-gate 	/* Set the dip in the soft state */
247*0Sstevel@tonic-gate 	softsp->dip = devi;
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	if ((softsp->portid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
250*0Sstevel@tonic-gate 	    DDI_PROP_DONTPASS, "portid", -1)) == -1) {
251*0Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to get %s property",
252*0Sstevel@tonic-gate 		    instance, "portid"));
253*0Sstevel@tonic-gate 		goto bad;
254*0Sstevel@tonic-gate 	}
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	DPRINTF(MC_ATTACH_DEBUG, ("mc%d ATTACH: portid %d, cpuid %d\n",
257*0Sstevel@tonic-gate 	    instance, softsp->portid, CPU->cpu_id));
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	/* map in the registers for this device. */
260*0Sstevel@tonic-gate 	if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0)) {
261*0Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to map registers",
262*0Sstevel@tonic-gate 		    instance));
263*0Sstevel@tonic-gate 		goto bad;
264*0Sstevel@tonic-gate 	}
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate 	/*
267*0Sstevel@tonic-gate 	 * Get the label of dimms and pin routing information at memory-layout
268*0Sstevel@tonic-gate 	 * property if the memory controller is enabled.
269*0Sstevel@tonic-gate 	 *
270*0Sstevel@tonic-gate 	 * Basically every memory-controller node on every machine should
271*0Sstevel@tonic-gate 	 * have one of these properties unless the memory controller is
272*0Sstevel@tonic-gate 	 * physically not capable of having memory attached to it, e.g.
273*0Sstevel@tonic-gate 	 * Excalibur's slave processor.
274*0Sstevel@tonic-gate 	 */
275*0Sstevel@tonic-gate 	err = ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip, DDI_PROP_DONTPASS,
276*0Sstevel@tonic-gate 	    "memory-layout", (caddr_t)&dimminfop, &len);
277*0Sstevel@tonic-gate 	if (err == DDI_PROP_SUCCESS) {
278*0Sstevel@tonic-gate 		/*
279*0Sstevel@tonic-gate 		 * Set the pointer and size of property in the soft state
280*0Sstevel@tonic-gate 		 */
281*0Sstevel@tonic-gate 		softsp->memlayoutp = dimminfop;
282*0Sstevel@tonic-gate 		softsp->size = len;
283*0Sstevel@tonic-gate 	} else if (err == DDI_PROP_NOT_FOUND) {
284*0Sstevel@tonic-gate 		/*
285*0Sstevel@tonic-gate 		 * This is a disable MC. Clear out the pointer and size
286*0Sstevel@tonic-gate 		 * of property in the soft state
287*0Sstevel@tonic-gate 		 */
288*0Sstevel@tonic-gate 		softsp->memlayoutp = NULL;
289*0Sstevel@tonic-gate 		softsp->size = 0;
290*0Sstevel@tonic-gate 	} else {
291*0Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc%d is disabled: dimminfop %p\n",
292*0Sstevel@tonic-gate 		    instance, dimminfop));
293*0Sstevel@tonic-gate 		goto bad2;
294*0Sstevel@tonic-gate 	}
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	DPRINTF(MC_ATTACH_DEBUG, ("mc%d: dimminfop=0x%p data=0x%llx len=%d\n",
297*0Sstevel@tonic-gate 	    instance, dimminfop, *dimminfop, len));
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate 	/* Get MC registers and construct all needed data structure */
300*0Sstevel@tonic-gate 	if (mc_get_mcregs(softsp) == -1)
301*0Sstevel@tonic-gate 		goto bad1;
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 	mutex_enter(&mcmutex);
304*0Sstevel@tonic-gate 	if (nmcs == 1) {
305*0Sstevel@tonic-gate 		if (&p2get_mem_unum)
306*0Sstevel@tonic-gate 			p2get_mem_unum = mc_get_mem_unum;
307*0Sstevel@tonic-gate 		if (&p2get_mem_info)
308*0Sstevel@tonic-gate 			p2get_mem_info = mc_get_mem_info;
309*0Sstevel@tonic-gate 	}
310*0Sstevel@tonic-gate 	mutex_exit(&mcmutex);
311*0Sstevel@tonic-gate 
312*0Sstevel@tonic-gate 	if (ddi_create_minor_node(devi, "mc-us3", S_IFCHR, instance,
313*0Sstevel@tonic-gate 	    "ddi_mem_ctrl", 0) != DDI_SUCCESS) {
314*0Sstevel@tonic-gate 		DPRINTF(MC_ATTACH_DEBUG, ("mc_attach: create_minor_node"
315*0Sstevel@tonic-gate 		    " failed \n"));
316*0Sstevel@tonic-gate 		goto bad1;
317*0Sstevel@tonic-gate 	}
318*0Sstevel@tonic-gate 
319*0Sstevel@tonic-gate 	ddi_report_dev(devi);
320*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
321*0Sstevel@tonic-gate 
322*0Sstevel@tonic-gate bad1:
323*0Sstevel@tonic-gate 	/* release all allocated data struture for this MC */
324*0Sstevel@tonic-gate 	mlayout_del(softsp->portid);
325*0Sstevel@tonic-gate 	if (softsp->memlayoutp != NULL)
326*0Sstevel@tonic-gate 		kmem_free(softsp->memlayoutp, softsp->size);
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 	/* remove the libdevinfo property */
329*0Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
330*0Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
331*0Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) == 1) {
332*0Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
333*0Sstevel@tonic-gate 			MEM_CFG_PROP_NAME);
334*0Sstevel@tonic-gate 	}
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate bad2:
337*0Sstevel@tonic-gate 	/* unmap the registers for this device. */
338*0Sstevel@tonic-gate 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
339*0Sstevel@tonic-gate 
340*0Sstevel@tonic-gate bad:
341*0Sstevel@tonic-gate 	ddi_soft_state_free(mcp, instance);
342*0Sstevel@tonic-gate 	return (DDI_FAILURE);
343*0Sstevel@tonic-gate }
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate /* ARGSUSED */
346*0Sstevel@tonic-gate static int
347*0Sstevel@tonic-gate mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
348*0Sstevel@tonic-gate {
349*0Sstevel@tonic-gate 	int instance;
350*0Sstevel@tonic-gate 	struct mc_soft_state *softsp;
351*0Sstevel@tonic-gate 
352*0Sstevel@tonic-gate 	/* get the instance of this devi */
353*0Sstevel@tonic-gate 	instance = ddi_get_instance(devi);
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 	/* get the soft state pointer for this device node */
356*0Sstevel@tonic-gate 	softsp = ddi_get_soft_state(mcp, instance);
357*0Sstevel@tonic-gate 
358*0Sstevel@tonic-gate 	switch (cmd) {
359*0Sstevel@tonic-gate 	case DDI_SUSPEND:
360*0Sstevel@tonic-gate 		return (DDI_SUCCESS);
361*0Sstevel@tonic-gate 
362*0Sstevel@tonic-gate 	case DDI_DETACH:
363*0Sstevel@tonic-gate 		break;
364*0Sstevel@tonic-gate 
365*0Sstevel@tonic-gate 	default:
366*0Sstevel@tonic-gate 		return (DDI_FAILURE);
367*0Sstevel@tonic-gate 	}
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 	DPRINTF(MC_DETACH_DEBUG, ("mc%d DETACH: portid= %d, table 0x%p\n",
370*0Sstevel@tonic-gate 	    instance, softsp->portid, softsp->memlayoutp));
371*0Sstevel@tonic-gate 
372*0Sstevel@tonic-gate 	/* remove the libdevinfo property */
373*0Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
374*0Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
375*0Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) == 1) {
376*0Sstevel@tonic-gate 		(void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip,
377*0Sstevel@tonic-gate 			MEM_CFG_PROP_NAME);
378*0Sstevel@tonic-gate 	}
379*0Sstevel@tonic-gate 
380*0Sstevel@tonic-gate 	/* release all allocated data struture for this MC */
381*0Sstevel@tonic-gate 	mlayout_del(softsp->portid);
382*0Sstevel@tonic-gate 	if (softsp->memlayoutp != NULL)
383*0Sstevel@tonic-gate 		kmem_free(softsp->memlayoutp, softsp->size);
384*0Sstevel@tonic-gate 
385*0Sstevel@tonic-gate 	/* unmap the registers */
386*0Sstevel@tonic-gate 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0);
387*0Sstevel@tonic-gate 
388*0Sstevel@tonic-gate 	mutex_enter(&mcmutex);
389*0Sstevel@tonic-gate 	if (nmcs == 0) {
390*0Sstevel@tonic-gate 		if (&p2get_mem_unum)
391*0Sstevel@tonic-gate 			p2get_mem_unum = NULL;
392*0Sstevel@tonic-gate 		if (&p2get_mem_info)
393*0Sstevel@tonic-gate 			p2get_mem_info = NULL;
394*0Sstevel@tonic-gate 	}
395*0Sstevel@tonic-gate 	mutex_exit(&mcmutex);
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 	ddi_remove_minor_node(devi, NULL);
398*0Sstevel@tonic-gate 
399*0Sstevel@tonic-gate 	/* free up the soft state */
400*0Sstevel@tonic-gate 	ddi_soft_state_free(mcp, instance);
401*0Sstevel@tonic-gate 
402*0Sstevel@tonic-gate 	return (DDI_SUCCESS);
403*0Sstevel@tonic-gate }
404*0Sstevel@tonic-gate 
405*0Sstevel@tonic-gate /* ARGSUSED */
406*0Sstevel@tonic-gate static int
407*0Sstevel@tonic-gate mc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
408*0Sstevel@tonic-gate {
409*0Sstevel@tonic-gate 	int status = 0;
410*0Sstevel@tonic-gate 
411*0Sstevel@tonic-gate 	/* verify that otyp is appropriate */
412*0Sstevel@tonic-gate 	if (otyp != OTYP_CHR) {
413*0Sstevel@tonic-gate 		return (EINVAL);
414*0Sstevel@tonic-gate 	}
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 	mutex_enter(&mcmutex);
417*0Sstevel@tonic-gate 	if (mc_is_open) {
418*0Sstevel@tonic-gate 		status = EBUSY;
419*0Sstevel@tonic-gate 		goto bad;
420*0Sstevel@tonic-gate 	}
421*0Sstevel@tonic-gate 	mc_is_open = 1;
422*0Sstevel@tonic-gate bad:
423*0Sstevel@tonic-gate 	mutex_exit(&mcmutex);
424*0Sstevel@tonic-gate 	return (status);
425*0Sstevel@tonic-gate }
426*0Sstevel@tonic-gate 
427*0Sstevel@tonic-gate /* ARGSUSED */
428*0Sstevel@tonic-gate static int
429*0Sstevel@tonic-gate mc_close(dev_t devp, int flag, int otyp, cred_t *credp)
430*0Sstevel@tonic-gate {
431*0Sstevel@tonic-gate 	mutex_enter(&mcmutex);
432*0Sstevel@tonic-gate 	mc_is_open = 0;
433*0Sstevel@tonic-gate 	mutex_exit(&mcmutex);
434*0Sstevel@tonic-gate 
435*0Sstevel@tonic-gate 	return (0);
436*0Sstevel@tonic-gate }
437*0Sstevel@tonic-gate 
438*0Sstevel@tonic-gate /*
439*0Sstevel@tonic-gate  * cmd includes MCIOC_MEMCONF, MCIOC_MEM, MCIOC_SEG, MCIOC_BANK, MCIOC_DEVGRP,
440*0Sstevel@tonic-gate  * MCIOC_CTRLCONF, MCIOC_CONTROL.
441*0Sstevel@tonic-gate  *
442*0Sstevel@tonic-gate  * MCIOC_MEM, MCIOC_SEG, MCIOC_CTRLCONF, and MCIOC_CONTROL are
443*0Sstevel@tonic-gate  * associated with various length struct. If given number is less than the
444*0Sstevel@tonic-gate  * number in kernel, update the number and return EINVAL so that user could
445*0Sstevel@tonic-gate  * allocate enough space for it.
446*0Sstevel@tonic-gate  *
447*0Sstevel@tonic-gate  */
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate /* ARGSUSED */
450*0Sstevel@tonic-gate static int
451*0Sstevel@tonic-gate mc_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p,
452*0Sstevel@tonic-gate 	int *rval_p)
453*0Sstevel@tonic-gate {
454*0Sstevel@tonic-gate 	size_t	size;
455*0Sstevel@tonic-gate 	struct mc_memconf mcmconf;
456*0Sstevel@tonic-gate 	struct mc_memory *mcmem, mcmem_in;
457*0Sstevel@tonic-gate 	struct mc_segment *mcseg, mcseg_in;
458*0Sstevel@tonic-gate 	struct mc_bank mcbank;
459*0Sstevel@tonic-gate 	struct mc_devgrp mcdevgrp;
460*0Sstevel@tonic-gate 	struct mc_ctrlconf *mcctrlconf, mcctrlconf_in;
461*0Sstevel@tonic-gate 	struct mc_control *mccontrol, mccontrol_in;
462*0Sstevel@tonic-gate 	struct seg_info *seg = NULL;
463*0Sstevel@tonic-gate 	struct bank_info *bank = NULL;
464*0Sstevel@tonic-gate 	struct dgrp_info *dgrp = NULL;
465*0Sstevel@tonic-gate 	struct mctrl_info *mcport;
466*0Sstevel@tonic-gate 	mc_dlist_t *mctrl;
467*0Sstevel@tonic-gate 	int i, status = 0;
468*0Sstevel@tonic-gate 	cpu_t *cpu;
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	switch (cmd) {
471*0Sstevel@tonic-gate 	case MCIOC_MEMCONF:
472*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 		mcmconf.nmcs = nmcs;
475*0Sstevel@tonic-gate 		mcmconf.nsegments = nsegments;
476*0Sstevel@tonic-gate 		mcmconf.nbanks = maxbanks;
477*0Sstevel@tonic-gate 		mcmconf.ndevgrps = NDGRPS;
478*0Sstevel@tonic-gate 		mcmconf.ndevs = NDIMMS;
479*0Sstevel@tonic-gate 		mcmconf.len_dev = MAX_DEVLEN;
480*0Sstevel@tonic-gate 		mcmconf.xfer_size = TRANSFER_SIZE;
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
483*0Sstevel@tonic-gate 
484*0Sstevel@tonic-gate 		if (copyout(&mcmconf, (void *)arg, sizeof (struct mc_memconf)))
485*0Sstevel@tonic-gate 			return (EFAULT);
486*0Sstevel@tonic-gate 		return (0);
487*0Sstevel@tonic-gate 
488*0Sstevel@tonic-gate 	/*
489*0Sstevel@tonic-gate 	 * input: nsegments and allocate space for various length of segmentids
490*0Sstevel@tonic-gate 	 *
491*0Sstevel@tonic-gate 	 * return    0: size, number of segments, and all segment ids,
492*0Sstevel@tonic-gate 	 *		where glocal and local ids are identical.
493*0Sstevel@tonic-gate 	 *	EINVAL: if the given nsegments is less than that in kernel and
494*0Sstevel@tonic-gate 	 *		nsegments of struct will be updated.
495*0Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
496*0Sstevel@tonic-gate 	 */
497*0Sstevel@tonic-gate 	case MCIOC_MEM:
498*0Sstevel@tonic-gate 		if (copyin((void *)arg, &mcmem_in,
499*0Sstevel@tonic-gate 		    sizeof (struct mc_memory)) != 0)
500*0Sstevel@tonic-gate 			return (EFAULT);
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
503*0Sstevel@tonic-gate 		if (mcmem_in.nsegments < nsegments) {
504*0Sstevel@tonic-gate 			mcmem_in.nsegments = nsegments;
505*0Sstevel@tonic-gate 			if (copyout(&mcmem_in, (void *)arg,
506*0Sstevel@tonic-gate 			    sizeof (struct mc_memory)))
507*0Sstevel@tonic-gate 				status = EFAULT;
508*0Sstevel@tonic-gate 			else
509*0Sstevel@tonic-gate 				status = EINVAL;
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
512*0Sstevel@tonic-gate 			return (status);
513*0Sstevel@tonic-gate 		}
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 		size = sizeof (struct mc_memory) + (nsegments - 1) *
516*0Sstevel@tonic-gate 		    sizeof (mcmem->segmentids[0]);
517*0Sstevel@tonic-gate 		mcmem = kmem_zalloc(size, KM_SLEEP);
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate 		mcmem->size = memsize;
520*0Sstevel@tonic-gate 		mcmem->nsegments = nsegments;
521*0Sstevel@tonic-gate 		seg = (struct seg_info *)seg_head;
522*0Sstevel@tonic-gate 		for (i = 0; i < nsegments; i++) {
523*0Sstevel@tonic-gate 			ASSERT(seg != NULL);
524*0Sstevel@tonic-gate 			mcmem->segmentids[i].globalid = seg->seg_node.id;
525*0Sstevel@tonic-gate 			mcmem->segmentids[i].localid = seg->seg_node.id;
526*0Sstevel@tonic-gate 			seg = (struct seg_info *)seg->seg_node.next;
527*0Sstevel@tonic-gate 		}
528*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
529*0Sstevel@tonic-gate 
530*0Sstevel@tonic-gate 		if (copyout(mcmem, (void *)arg, size))
531*0Sstevel@tonic-gate 			status = EFAULT;
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 		kmem_free(mcmem, size);
534*0Sstevel@tonic-gate 		return (status);
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 	/*
537*0Sstevel@tonic-gate 	 * input: id, nbanks and allocate space for various length of bankids
538*0Sstevel@tonic-gate 	 *
539*0Sstevel@tonic-gate 	 * return    0: base, size, number of banks, and all bank ids,
540*0Sstevel@tonic-gate 	 *		where global id is unique of all banks and local id
541*0Sstevel@tonic-gate 	 *		is only unique for mc.
542*0Sstevel@tonic-gate 	 *	EINVAL: either id isn't found or if given nbanks is less than
543*0Sstevel@tonic-gate 	 *		that in kernel and nbanks of struct will be updated.
544*0Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
545*0Sstevel@tonic-gate 	 */
546*0Sstevel@tonic-gate 	case MCIOC_SEG:
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 		if (copyin((void *)arg, &mcseg_in,
549*0Sstevel@tonic-gate 		    sizeof (struct mc_segment)) != 0)
550*0Sstevel@tonic-gate 			return (EFAULT);
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
553*0Sstevel@tonic-gate 		if ((seg = (struct seg_info *)mc_node_get(mcseg_in.id,
554*0Sstevel@tonic-gate 		    seg_head)) == NULL) {
555*0Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG: seg not match, "
556*0Sstevel@tonic-gate 			    "id %d\n", mcseg_in.id));
557*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
558*0Sstevel@tonic-gate 			return (EFAULT);
559*0Sstevel@tonic-gate 		}
560*0Sstevel@tonic-gate 
561*0Sstevel@tonic-gate 		if (mcseg_in.nbanks < seg->nbanks) {
562*0Sstevel@tonic-gate 			mcseg_in.nbanks = seg->nbanks;
563*0Sstevel@tonic-gate 			if (copyout(&mcseg_in, (void *)arg,
564*0Sstevel@tonic-gate 			    sizeof (struct mc_segment)))
565*0Sstevel@tonic-gate 				status = EFAULT;
566*0Sstevel@tonic-gate 			else
567*0Sstevel@tonic-gate 				status = EINVAL;
568*0Sstevel@tonic-gate 
569*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
570*0Sstevel@tonic-gate 			return (status);
571*0Sstevel@tonic-gate 		}
572*0Sstevel@tonic-gate 
573*0Sstevel@tonic-gate 		size = sizeof (struct mc_segment) + (seg->nbanks - 1) *
574*0Sstevel@tonic-gate 		    sizeof (mcseg->bankids[0]);
575*0Sstevel@tonic-gate 		mcseg = kmem_zalloc(size, KM_SLEEP);
576*0Sstevel@tonic-gate 
577*0Sstevel@tonic-gate 		mcseg->id = seg->seg_node.id;
578*0Sstevel@tonic-gate 		mcseg->ifactor = seg->ifactor;
579*0Sstevel@tonic-gate 		mcseg->base = seg->base;
580*0Sstevel@tonic-gate 		mcseg->size = seg->size;
581*0Sstevel@tonic-gate 		mcseg->nbanks = seg->nbanks;
582*0Sstevel@tonic-gate 
583*0Sstevel@tonic-gate 		bank = seg->hb_inseg;
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:nbanks %d seg 0x%p bank %p\n",
586*0Sstevel@tonic-gate 		    seg->nbanks, seg, bank));
587*0Sstevel@tonic-gate 
588*0Sstevel@tonic-gate 		i = 0;
589*0Sstevel@tonic-gate 		while (bank != NULL) {
590*0Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:idx %d bank_id %d\n",
591*0Sstevel@tonic-gate 			    i, bank->bank_node.id));
592*0Sstevel@tonic-gate 			mcseg->bankids[i].globalid = bank->bank_node.id;
593*0Sstevel@tonic-gate 			mcseg->bankids[i++].localid =
594*0Sstevel@tonic-gate 			    bank->local_id;
595*0Sstevel@tonic-gate 			bank = bank->n_inseg;
596*0Sstevel@tonic-gate 		}
597*0Sstevel@tonic-gate 		ASSERT(i == seg->nbanks);
598*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 		if (copyout(mcseg, (void *)arg, size))
601*0Sstevel@tonic-gate 			status = EFAULT;
602*0Sstevel@tonic-gate 
603*0Sstevel@tonic-gate 		kmem_free(mcseg, size);
604*0Sstevel@tonic-gate 		return (status);
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 	/*
607*0Sstevel@tonic-gate 	 * input: id
608*0Sstevel@tonic-gate 	 *
609*0Sstevel@tonic-gate 	 * return    0: mask, match, size, and devgrpid,
610*0Sstevel@tonic-gate 	 *		where global id is unique of all devgrps and local id
611*0Sstevel@tonic-gate 	 *		is only unique for mc.
612*0Sstevel@tonic-gate 	 *	EINVAL: if id isn't found
613*0Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
614*0Sstevel@tonic-gate 	 */
615*0Sstevel@tonic-gate 	case MCIOC_BANK:
616*0Sstevel@tonic-gate 		if (copyin((void *)arg, &mcbank, sizeof (struct mc_bank)) != 0)
617*0Sstevel@tonic-gate 			return (EFAULT);
618*0Sstevel@tonic-gate 
619*0Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank id %d\n", mcbank.id));
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
622*0Sstevel@tonic-gate 
623*0Sstevel@tonic-gate 		if ((bank = (struct bank_info *)mc_node_get(mcbank.id,
624*0Sstevel@tonic-gate 		    bank_head)) == NULL) {
625*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
626*0Sstevel@tonic-gate 			return (EINVAL);
627*0Sstevel@tonic-gate 		}
628*0Sstevel@tonic-gate 
629*0Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank 0x%p valid %d\n",
630*0Sstevel@tonic-gate 		    bank->bank_node.id, bank, bank->valid));
631*0Sstevel@tonic-gate 
632*0Sstevel@tonic-gate 		/*
633*0Sstevel@tonic-gate 		 * If (Physic Address & MASK) == MATCH, Physic Address is
634*0Sstevel@tonic-gate 		 * located at this bank. The lower physical address bits
635*0Sstevel@tonic-gate 		 * are at [9-6].
636*0Sstevel@tonic-gate 		 */
637*0Sstevel@tonic-gate 		mcbank.mask = (~(bank->lk | ~(MADR_LK_MASK >>
638*0Sstevel@tonic-gate 		    MADR_LK_SHIFT))) << MADR_LPA_SHIFT;
639*0Sstevel@tonic-gate 		mcbank.match = bank->lm << MADR_LPA_SHIFT;
640*0Sstevel@tonic-gate 		mcbank.size = bank->size;
641*0Sstevel@tonic-gate 		mcbank.devgrpid.globalid = bank->devgrp_id;
642*0Sstevel@tonic-gate 		mcbank.devgrpid.localid = bank->devgrp_id % NDGRPS;
643*0Sstevel@tonic-gate 
644*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 		if (copyout(&mcbank, (void *)arg, sizeof (struct mc_bank)))
647*0Sstevel@tonic-gate 			return (EFAULT);
648*0Sstevel@tonic-gate 		return (0);
649*0Sstevel@tonic-gate 
650*0Sstevel@tonic-gate 	/*
651*0Sstevel@tonic-gate 	 * input:id and allocate space for various length of deviceids
652*0Sstevel@tonic-gate 	 *
653*0Sstevel@tonic-gate 	 * return    0: size and number of devices.
654*0Sstevel@tonic-gate 	 *	EINVAL: id isn't found
655*0Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
656*0Sstevel@tonic-gate 	 */
657*0Sstevel@tonic-gate 	case MCIOC_DEVGRP:
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 		if (copyin((void *)arg, &mcdevgrp,
660*0Sstevel@tonic-gate 		    sizeof (struct mc_devgrp)) != 0)
661*0Sstevel@tonic-gate 			return (EFAULT);
662*0Sstevel@tonic-gate 
663*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
664*0Sstevel@tonic-gate 		if ((dgrp = (struct dgrp_info *)mc_node_get(mcdevgrp.id,
665*0Sstevel@tonic-gate 		    dgrp_head)) == NULL) {
666*0Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_DEVGRP: not match, id "
667*0Sstevel@tonic-gate 			    "%d\n", mcdevgrp.id));
668*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
669*0Sstevel@tonic-gate 			return (EINVAL);
670*0Sstevel@tonic-gate 		}
671*0Sstevel@tonic-gate 
672*0Sstevel@tonic-gate 		mcdevgrp.ndevices = dgrp->ndevices;
673*0Sstevel@tonic-gate 		mcdevgrp.size = dgrp->size;
674*0Sstevel@tonic-gate 
675*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
676*0Sstevel@tonic-gate 
677*0Sstevel@tonic-gate 		if (copyout(&mcdevgrp, (void *)arg, sizeof (struct mc_devgrp)))
678*0Sstevel@tonic-gate 			status = EFAULT;
679*0Sstevel@tonic-gate 
680*0Sstevel@tonic-gate 		return (status);
681*0Sstevel@tonic-gate 
682*0Sstevel@tonic-gate 	/*
683*0Sstevel@tonic-gate 	 * input: nmcs and allocate space for various length of mcids
684*0Sstevel@tonic-gate 	 *
685*0Sstevel@tonic-gate 	 * return    0: number of mc, and all mcids,
686*0Sstevel@tonic-gate 	 *		where glocal and local ids are identical.
687*0Sstevel@tonic-gate 	 *	EINVAL: if the given nmcs is less than that in kernel and
688*0Sstevel@tonic-gate 	 *		nmcs of struct will be updated.
689*0Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
690*0Sstevel@tonic-gate 	 */
691*0Sstevel@tonic-gate 	case MCIOC_CTRLCONF:
692*0Sstevel@tonic-gate 		if (copyin((void *)arg, &mcctrlconf_in,
693*0Sstevel@tonic-gate 		    sizeof (struct mc_ctrlconf)) != 0)
694*0Sstevel@tonic-gate 			return (EFAULT);
695*0Sstevel@tonic-gate 
696*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
697*0Sstevel@tonic-gate 		if (mcctrlconf_in.nmcs < nmcs) {
698*0Sstevel@tonic-gate 			mcctrlconf_in.nmcs = nmcs;
699*0Sstevel@tonic-gate 			if (copyout(&mcctrlconf_in, (void *)arg,
700*0Sstevel@tonic-gate 			    sizeof (struct mc_ctrlconf)))
701*0Sstevel@tonic-gate 				status = EFAULT;
702*0Sstevel@tonic-gate 			else
703*0Sstevel@tonic-gate 				status = EINVAL;
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
706*0Sstevel@tonic-gate 			return (status);
707*0Sstevel@tonic-gate 		}
708*0Sstevel@tonic-gate 
709*0Sstevel@tonic-gate 		/*
710*0Sstevel@tonic-gate 		 * Cannot just use the size of the struct because of the various
711*0Sstevel@tonic-gate 		 * length struct
712*0Sstevel@tonic-gate 		 */
713*0Sstevel@tonic-gate 		size = sizeof (struct mc_ctrlconf) + ((nmcs - 1) *
714*0Sstevel@tonic-gate 		    sizeof (mcctrlconf->mcids[0]));
715*0Sstevel@tonic-gate 		mcctrlconf = kmem_zalloc(size, KM_SLEEP);
716*0Sstevel@tonic-gate 
717*0Sstevel@tonic-gate 		mcctrlconf->nmcs = nmcs;
718*0Sstevel@tonic-gate 
719*0Sstevel@tonic-gate 		/* Get all MC ids and add to mcctrlconf */
720*0Sstevel@tonic-gate 		mctrl = mctrl_head;
721*0Sstevel@tonic-gate 		i = 0;
722*0Sstevel@tonic-gate 		while (mctrl != NULL) {
723*0Sstevel@tonic-gate 			mcctrlconf->mcids[i].globalid = mctrl->id;
724*0Sstevel@tonic-gate 			mcctrlconf->mcids[i].localid = mctrl->id;
725*0Sstevel@tonic-gate 			i++;
726*0Sstevel@tonic-gate 			mctrl = mctrl->next;
727*0Sstevel@tonic-gate 		}
728*0Sstevel@tonic-gate 		ASSERT(i == nmcs);
729*0Sstevel@tonic-gate 
730*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
731*0Sstevel@tonic-gate 
732*0Sstevel@tonic-gate 		if (copyout(mcctrlconf, (void *)arg, size))
733*0Sstevel@tonic-gate 			status = EFAULT;
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 		kmem_free(mcctrlconf, size);
736*0Sstevel@tonic-gate 		return (status);
737*0Sstevel@tonic-gate 
738*0Sstevel@tonic-gate 	/*
739*0Sstevel@tonic-gate 	 * input:id, ndevgrps and allocate space for various length of devgrpids
740*0Sstevel@tonic-gate 	 *
741*0Sstevel@tonic-gate 	 * return    0: number of devgrp, and all devgrpids,
742*0Sstevel@tonic-gate 	 *		is unique of all devgrps and local id is only unique
743*0Sstevel@tonic-gate 	 *		for mc.
744*0Sstevel@tonic-gate 	 *	EINVAL: either if id isn't found or if the given ndevgrps is
745*0Sstevel@tonic-gate 	 *		less than that in kernel and ndevgrps of struct will
746*0Sstevel@tonic-gate 	 *		be updated.
747*0Sstevel@tonic-gate 	 *	EFAULT: if other errors in kernel.
748*0Sstevel@tonic-gate 	 */
749*0Sstevel@tonic-gate 	case MCIOC_CONTROL:
750*0Sstevel@tonic-gate 		if (copyin((void *)arg, &mccontrol_in,
751*0Sstevel@tonic-gate 		    sizeof (struct mc_control)) != 0)
752*0Sstevel@tonic-gate 			return (EFAULT);
753*0Sstevel@tonic-gate 
754*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
755*0Sstevel@tonic-gate 		if ((mcport = (struct mctrl_info *)mc_node_get(mccontrol_in.id,
756*0Sstevel@tonic-gate 		    mctrl_head)) == NULL) {
757*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
758*0Sstevel@tonic-gate 			return (EINVAL);
759*0Sstevel@tonic-gate 		}
760*0Sstevel@tonic-gate 
761*0Sstevel@tonic-gate 		/*
762*0Sstevel@tonic-gate 		 * mcport->ndevgrps zero means Memory Controller is disable.
763*0Sstevel@tonic-gate 		 */
764*0Sstevel@tonic-gate 		if ((mccontrol_in.ndevgrps < mcport->ndevgrps) ||
765*0Sstevel@tonic-gate 		    (mcport->ndevgrps == 0)) {
766*0Sstevel@tonic-gate 			mccontrol_in.ndevgrps = mcport->ndevgrps;
767*0Sstevel@tonic-gate 			if (copyout(&mccontrol_in, (void *)arg,
768*0Sstevel@tonic-gate 			    sizeof (struct mc_control)))
769*0Sstevel@tonic-gate 				status = EFAULT;
770*0Sstevel@tonic-gate 			else if (mcport->ndevgrps != 0)
771*0Sstevel@tonic-gate 				status = EINVAL;
772*0Sstevel@tonic-gate 
773*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
774*0Sstevel@tonic-gate 			return (status);
775*0Sstevel@tonic-gate 		}
776*0Sstevel@tonic-gate 
777*0Sstevel@tonic-gate 		size = sizeof (struct mc_control) + (mcport->ndevgrps - 1) *
778*0Sstevel@tonic-gate 		    sizeof (mccontrol->devgrpids[0]);
779*0Sstevel@tonic-gate 		mccontrol = kmem_zalloc(size, KM_SLEEP);
780*0Sstevel@tonic-gate 
781*0Sstevel@tonic-gate 		mccontrol->id = mcport->mctrl_node.id;
782*0Sstevel@tonic-gate 		mccontrol->ndevgrps = mcport->ndevgrps;
783*0Sstevel@tonic-gate 		for (i = 0; i < mcport->ndevgrps; i++) {
784*0Sstevel@tonic-gate 			mccontrol->devgrpids[i].globalid = mcport->devgrpids[i];
785*0Sstevel@tonic-gate 			mccontrol->devgrpids[i].localid =
786*0Sstevel@tonic-gate 			    mcport->devgrpids[i] % NDGRPS;
787*0Sstevel@tonic-gate 			DPRINTF(MC_CMD_DEBUG, ("MCIOC_CONTROL: devgrp id %d\n",
788*0Sstevel@tonic-gate 			    mccontrol->devgrpids[i]));
789*0Sstevel@tonic-gate 		}
790*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
791*0Sstevel@tonic-gate 
792*0Sstevel@tonic-gate 		if (copyout(mccontrol, (void *)arg, size))
793*0Sstevel@tonic-gate 			status = EFAULT;
794*0Sstevel@tonic-gate 
795*0Sstevel@tonic-gate 		kmem_free(mccontrol, size);
796*0Sstevel@tonic-gate 		return (status);
797*0Sstevel@tonic-gate 
798*0Sstevel@tonic-gate 	/*
799*0Sstevel@tonic-gate 	 * input:id
800*0Sstevel@tonic-gate 	 *
801*0Sstevel@tonic-gate 	 * return    0: CPU flushed successfully.
802*0Sstevel@tonic-gate 	 *	EINVAL: the id wasn't found
803*0Sstevel@tonic-gate 	 */
804*0Sstevel@tonic-gate 	case MCIOC_ECFLUSH:
805*0Sstevel@tonic-gate 		mutex_enter(&cpu_lock);
806*0Sstevel@tonic-gate 		cpu = cpu_get((processorid_t)arg);
807*0Sstevel@tonic-gate 		mutex_exit(&cpu_lock);
808*0Sstevel@tonic-gate 		if (cpu == NULL)
809*0Sstevel@tonic-gate 			return (EINVAL);
810*0Sstevel@tonic-gate 
811*0Sstevel@tonic-gate 		xc_one(arg, (xcfunc_t *)cpu_flush_ecache, 0, 0);
812*0Sstevel@tonic-gate 
813*0Sstevel@tonic-gate 		return (0);
814*0Sstevel@tonic-gate 
815*0Sstevel@tonic-gate 	default:
816*0Sstevel@tonic-gate 		DPRINTF(MC_CMD_DEBUG, ("DEFAULT: cmd is wrong\n"));
817*0Sstevel@tonic-gate 		return (EFAULT);
818*0Sstevel@tonic-gate 	}
819*0Sstevel@tonic-gate }
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate /*
822*0Sstevel@tonic-gate  * Get Memory Address Decoding Registers and construct list.
823*0Sstevel@tonic-gate  * flag is to workaround Cheetah's restriction where register cannot be mapped
824*0Sstevel@tonic-gate  * if port id(MC registers on it) == cpu id(process is running on it).
825*0Sstevel@tonic-gate  */
826*0Sstevel@tonic-gate static int
827*0Sstevel@tonic-gate mc_get_mcregs(struct mc_soft_state *softsp)
828*0Sstevel@tonic-gate {
829*0Sstevel@tonic-gate 	int i;
830*0Sstevel@tonic-gate 	int err = 0;
831*0Sstevel@tonic-gate 	uint64_t madreg;
832*0Sstevel@tonic-gate 	uint64_t ma_reg_array[NBANKS];	/* there are NBANKS of madrs */
833*0Sstevel@tonic-gate 
834*0Sstevel@tonic-gate 	/* Construct lists for MC, mctrl_info, dgrp_info, and device_info */
835*0Sstevel@tonic-gate 	mc_construct(softsp->portid, softsp->memlayoutp);
836*0Sstevel@tonic-gate 
837*0Sstevel@tonic-gate 	/*
838*0Sstevel@tonic-gate 	 * If memlayoutp is NULL, the Memory Controller is disable, and
839*0Sstevel@tonic-gate 	 * doesn't need to create any bank and segment.
840*0Sstevel@tonic-gate 	 */
841*0Sstevel@tonic-gate 	if (softsp->memlayoutp == NULL)
842*0Sstevel@tonic-gate 		goto exit;
843*0Sstevel@tonic-gate 
844*0Sstevel@tonic-gate 	/*
845*0Sstevel@tonic-gate 	 * Get the content of 4 Memory Address Decoding Registers, and
846*0Sstevel@tonic-gate 	 * construct lists of logical banks and segments.
847*0Sstevel@tonic-gate 	 */
848*0Sstevel@tonic-gate 	for (i = 0; i < NBANKS; i++) {
849*0Sstevel@tonic-gate 		DPRINTF(MC_REG_DEBUG, ("get_mcregs: mapreg=0x%p portid=%d "
850*0Sstevel@tonic-gate 		    "cpu=%d\n", softsp->mc_base, softsp->portid, CPU->cpu_id));
851*0Sstevel@tonic-gate 
852*0Sstevel@tonic-gate 		kpreempt_disable();
853*0Sstevel@tonic-gate 		if (softsp->portid == (cpunodes[CPU->cpu_id].portid))
854*0Sstevel@tonic-gate 			madreg = get_mcr(MADR0OFFSET + (i * REGOFFSET));
855*0Sstevel@tonic-gate 		else
856*0Sstevel@tonic-gate 			madreg = *((uint64_t *)(softsp->mc_base + MADR0OFFSET +
857*0Sstevel@tonic-gate 			    (i * REGOFFSET)));
858*0Sstevel@tonic-gate 		kpreempt_enable();
859*0Sstevel@tonic-gate 
860*0Sstevel@tonic-gate 		DPRINTF(MC_REG_DEBUG, ("get_mcregs 2: memlayoutp=0x%p madreg "
861*0Sstevel@tonic-gate 		    "reg=0x%llx\n", softsp->memlayoutp, madreg));
862*0Sstevel@tonic-gate 
863*0Sstevel@tonic-gate 		ma_reg_array[i] = madreg;
864*0Sstevel@tonic-gate 
865*0Sstevel@tonic-gate 		if ((err = mlayout_add(softsp->portid, i, madreg,
866*0Sstevel@tonic-gate 		    softsp->memlayoutp)) == -1)
867*0Sstevel@tonic-gate 			break;
868*0Sstevel@tonic-gate 	}
869*0Sstevel@tonic-gate 
870*0Sstevel@tonic-gate 	/*
871*0Sstevel@tonic-gate 	 * Create the logical bank property for this mc node. This
872*0Sstevel@tonic-gate 	 * property is an encoded array of the madr for each logical
873*0Sstevel@tonic-gate 	 * bank (there are NBANKS of these).
874*0Sstevel@tonic-gate 	 */
875*0Sstevel@tonic-gate 	if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip,
876*0Sstevel@tonic-gate 	    DDI_PROP_NOTPROM | DDI_PROP_DONTPASS,
877*0Sstevel@tonic-gate 	    MEM_CFG_PROP_NAME) != 1) {
878*0Sstevel@tonic-gate 		(void) ddi_prop_create(DDI_DEV_T_NONE, softsp->dip,
879*0Sstevel@tonic-gate 			DDI_PROP_CANSLEEP, MEM_CFG_PROP_NAME,
880*0Sstevel@tonic-gate 			(caddr_t)&ma_reg_array, sizeof (ma_reg_array));
881*0Sstevel@tonic-gate 	}
882*0Sstevel@tonic-gate 
883*0Sstevel@tonic-gate exit:
884*0Sstevel@tonic-gate 	if (!err) {
885*0Sstevel@tonic-gate 		mutex_enter(&mcdatamutex);
886*0Sstevel@tonic-gate 		nmcs++;
887*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
888*0Sstevel@tonic-gate 	}
889*0Sstevel@tonic-gate 	return (err);
890*0Sstevel@tonic-gate }
891*0Sstevel@tonic-gate 
892*0Sstevel@tonic-gate /*
893*0Sstevel@tonic-gate  * A cache line is composed of four quadwords with the associated ECC, the
894*0Sstevel@tonic-gate  * MTag along with its associated ECC. This is depicted below:
895*0Sstevel@tonic-gate  *
896*0Sstevel@tonic-gate  * |                    Data                    |   ECC   | Mtag |MTag ECC|
897*0Sstevel@tonic-gate  *  127                                         0 8       0 2    0 3      0
898*0Sstevel@tonic-gate  *
899*0Sstevel@tonic-gate  * synd_code will be mapped as the following order to mc_get_mem_unum.
900*0Sstevel@tonic-gate  *  143                                         16        7      4        0
901*0Sstevel@tonic-gate  *
902*0Sstevel@tonic-gate  * |  Quadword  0  |  Quadword  1  |  Quadword  2  |  Quadword  3  |
903*0Sstevel@tonic-gate  *  575         432 431         288 287         144 143		   0
904*0Sstevel@tonic-gate  *
905*0Sstevel@tonic-gate  * dimm table: each bit at a cache line needs two bits to present one of
906*0Sstevel@tonic-gate  *      four dimms. So it needs 144 bytes(576 * 2 / 8). The content is in
907*0Sstevel@tonic-gate  *      big edian order, i.e. dimm_table[0] presents for bit 572 to 575.
908*0Sstevel@tonic-gate  *
909*0Sstevel@tonic-gate  * pin table: each bit at a cache line needs one byte to present pin position,
910*0Sstevel@tonic-gate  *      where max. is 230. So it needs 576 bytes. The order of table index is
911*0Sstevel@tonic-gate  *      the same as bit position at a cache line, i.e. pin_table[0] presents
912*0Sstevel@tonic-gate  *      for bit 0, Mtag ECC 0 of Quadword 3.
913*0Sstevel@tonic-gate  *
914*0Sstevel@tonic-gate  * This is a mapping from syndrome code to QuadWord Logical layout at Safari.
915*0Sstevel@tonic-gate  * Referring to Figure 3-4, Excalibur Architecture Manual.
916*0Sstevel@tonic-gate  * This table could be moved to cheetah.c if other platform teams agree with
917*0Sstevel@tonic-gate  * the bit layout at QuadWord.
918*0Sstevel@tonic-gate  */
919*0Sstevel@tonic-gate 
920*0Sstevel@tonic-gate static uint8_t qwordmap[] =
921*0Sstevel@tonic-gate {
922*0Sstevel@tonic-gate 16,   17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
923*0Sstevel@tonic-gate 32,   33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
924*0Sstevel@tonic-gate 48,   49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
925*0Sstevel@tonic-gate 64,   65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
926*0Sstevel@tonic-gate 80,   81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,
927*0Sstevel@tonic-gate 96,   97,  98,  99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
928*0Sstevel@tonic-gate 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
929*0Sstevel@tonic-gate 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
930*0Sstevel@tonic-gate 7,    8,   9,  10,  11,  12,  13,  14,  15,   4,   5,   6,   0,   1,   2,   3,
931*0Sstevel@tonic-gate };
932*0Sstevel@tonic-gate 
933*0Sstevel@tonic-gate #define	QWORD_SIZE	144
934*0Sstevel@tonic-gate 
935*0Sstevel@tonic-gate /* ARGSUSED */
936*0Sstevel@tonic-gate static int
937*0Sstevel@tonic-gate mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf, int buflen, int *lenp)
938*0Sstevel@tonic-gate {
939*0Sstevel@tonic-gate 	int i, upper_pa, lower_pa, dimmoffset;
940*0Sstevel@tonic-gate 	int quadword, pos_cacheline, position, index, idx4dimm;
941*0Sstevel@tonic-gate 	int qwlayout = synd_code;
942*0Sstevel@tonic-gate 	short offset, data;
943*0Sstevel@tonic-gate 	char unum[UNUM_NAMLEN];
944*0Sstevel@tonic-gate 	struct dimm_info *dimmp;
945*0Sstevel@tonic-gate 	struct pin_info *pinp;
946*0Sstevel@tonic-gate 	struct bank_info *bank;
947*0Sstevel@tonic-gate 
948*0Sstevel@tonic-gate 	/*
949*0Sstevel@tonic-gate 	 * Enforce old Openboot requirement for synd code, either a single-bit
950*0Sstevel@tonic-gate 	 * code from 0..QWORD_SIZE-1 or -1 (multi-bit error).
951*0Sstevel@tonic-gate 	 */
952*0Sstevel@tonic-gate 	if (qwlayout < -1 || qwlayout >= QWORD_SIZE)
953*0Sstevel@tonic-gate 		return (EINVAL);
954*0Sstevel@tonic-gate 
955*0Sstevel@tonic-gate 	unum[0] = '\0';
956*0Sstevel@tonic-gate 
957*0Sstevel@tonic-gate 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
958*0Sstevel@tonic-gate 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
959*0Sstevel@tonic-gate 
960*0Sstevel@tonic-gate 	DPRINTF(MC_GUNUM_DEBUG, ("qwlayout %d\n", qwlayout));
961*0Sstevel@tonic-gate 
962*0Sstevel@tonic-gate 	/*
963*0Sstevel@tonic-gate 	 * Scan all logical banks to get one responding to the physical
964*0Sstevel@tonic-gate 	 * address. Then compute the index to look up dimm and pin tables
965*0Sstevel@tonic-gate 	 * to generate the unmuber.
966*0Sstevel@tonic-gate 	 */
967*0Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
968*0Sstevel@tonic-gate 	bank = (struct bank_info *)bank_head;
969*0Sstevel@tonic-gate 	while (bank != NULL) {
970*0Sstevel@tonic-gate 		int bankid, mcid, bankno_permc;
971*0Sstevel@tonic-gate 
972*0Sstevel@tonic-gate 		bankid = bank->bank_node.id;
973*0Sstevel@tonic-gate 		bankno_permc = bankid % NBANKS;
974*0Sstevel@tonic-gate 		mcid = bankid / NBANKS;
975*0Sstevel@tonic-gate 
976*0Sstevel@tonic-gate 		/*
977*0Sstevel@tonic-gate 		 * The Address Decoding logic decodes the different fields
978*0Sstevel@tonic-gate 		 * in the Memory Address Drcoding register to determine
979*0Sstevel@tonic-gate 		 * whether a particular logic bank should respond to a
980*0Sstevel@tonic-gate 		 * physical address.
981*0Sstevel@tonic-gate 		 */
982*0Sstevel@tonic-gate 		if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) |
983*0Sstevel@tonic-gate 		    bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) {
984*0Sstevel@tonic-gate 			bank = (struct bank_info *)bank->bank_node.next;
985*0Sstevel@tonic-gate 			continue;
986*0Sstevel@tonic-gate 		}
987*0Sstevel@tonic-gate 
988*0Sstevel@tonic-gate 		dimmoffset = (bankno_permc % NDGRPS) * NDIMMS;
989*0Sstevel@tonic-gate 
990*0Sstevel@tonic-gate 		dimmp = (struct dimm_info *)bank->dimminfop;
991*0Sstevel@tonic-gate 		ASSERT(dimmp != NULL);
992*0Sstevel@tonic-gate 
993*0Sstevel@tonic-gate 		if ((qwlayout >= 0) && (qwlayout < QWORD_SIZE)) {
994*0Sstevel@tonic-gate 			/*
995*0Sstevel@tonic-gate 			 * single-bit error handling, we can identify specific
996*0Sstevel@tonic-gate 			 * DIMM.
997*0Sstevel@tonic-gate 			 */
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate 			pinp = (struct pin_info *)&dimmp->data[0];
1000*0Sstevel@tonic-gate 
1001*0Sstevel@tonic-gate 			if (!dimmp->sym_flag)
1002*0Sstevel@tonic-gate 				pinp++;
1003*0Sstevel@tonic-gate 
1004*0Sstevel@tonic-gate 			quadword = (paddr & 0x3f) / 16;
1005*0Sstevel@tonic-gate 			/* or quadword = (paddr >> 4) % 4; */
1006*0Sstevel@tonic-gate 			pos_cacheline = ((3 - quadword) * 144) +
1007*0Sstevel@tonic-gate 			    qwordmap[qwlayout];
1008*0Sstevel@tonic-gate 			position = 575 - pos_cacheline;
1009*0Sstevel@tonic-gate 			index = position * 2 / 8;
1010*0Sstevel@tonic-gate 			offset = position % 4;
1011*0Sstevel@tonic-gate 
1012*0Sstevel@tonic-gate 			/*
1013*0Sstevel@tonic-gate 			 * Trade-off: We cound't add pin number to
1014*0Sstevel@tonic-gate 			 * unumber string because statistic number
1015*0Sstevel@tonic-gate 			 * pumps up at the corresponding dimm not pin.
1016*0Sstevel@tonic-gate 			 * (void) sprintf(unum, "Pin %1u ", (uint_t)
1017*0Sstevel@tonic-gate 			 * pinp->pintable[pos_cacheline]);
1018*0Sstevel@tonic-gate 			 */
1019*0Sstevel@tonic-gate 			DPRINTF(MC_GUNUM_DEBUG, ("Pin number %1u\n",
1020*0Sstevel@tonic-gate 			    (uint_t)pinp->pintable[pos_cacheline]));
1021*0Sstevel@tonic-gate 			data = pinp->dimmtable[index];
1022*0Sstevel@tonic-gate 			idx4dimm = (data >> ((3 - offset) * 2)) & 3;
1023*0Sstevel@tonic-gate 
1024*0Sstevel@tonic-gate 			(void) strncpy(unum,
1025*0Sstevel@tonic-gate 			    (char *)dimmp->label[dimmoffset + idx4dimm],
1026*0Sstevel@tonic-gate 			    UNUM_NAMLEN);
1027*0Sstevel@tonic-gate 			DPRINTF(MC_GUNUM_DEBUG, ("unum %s\n", unum));
1028*0Sstevel@tonic-gate 			/*
1029*0Sstevel@tonic-gate 			 * platform hook for adding label information to unum.
1030*0Sstevel@tonic-gate 			 */
1031*0Sstevel@tonic-gate 			mc_add_mem_unum_label(unum, mcid, bankno_permc,
1032*0Sstevel@tonic-gate 			    idx4dimm);
1033*0Sstevel@tonic-gate 		} else {
1034*0Sstevel@tonic-gate 			char *p = unum;
1035*0Sstevel@tonic-gate 			size_t res = UNUM_NAMLEN;
1036*0Sstevel@tonic-gate 
1037*0Sstevel@tonic-gate 			/*
1038*0Sstevel@tonic-gate 			 * multi-bit error handling, we can only identify
1039*0Sstevel@tonic-gate 			 * bank of DIMMs.
1040*0Sstevel@tonic-gate 			 */
1041*0Sstevel@tonic-gate 
1042*0Sstevel@tonic-gate 			for (i = 0; (i < NDIMMS) && (res > 0); i++) {
1043*0Sstevel@tonic-gate 				(void) snprintf(p, res, "%s%s",
1044*0Sstevel@tonic-gate 				    i == 0 ? "" : " ",
1045*0Sstevel@tonic-gate 				    (char *)dimmp->label[dimmoffset + i]);
1046*0Sstevel@tonic-gate 				res -= strlen(p);
1047*0Sstevel@tonic-gate 				p += strlen(p);
1048*0Sstevel@tonic-gate 			}
1049*0Sstevel@tonic-gate 
1050*0Sstevel@tonic-gate 			/*
1051*0Sstevel@tonic-gate 			 * platform hook for adding label information
1052*0Sstevel@tonic-gate 			 * to unum.
1053*0Sstevel@tonic-gate 			 */
1054*0Sstevel@tonic-gate 			mc_add_mem_unum_label(unum, mcid, bankno_permc, -1);
1055*0Sstevel@tonic-gate 		}
1056*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
1057*0Sstevel@tonic-gate 		if ((strlen(unum) >= UNUM_NAMLEN) ||
1058*0Sstevel@tonic-gate 		    (strlen(unum) >= buflen)) {
1059*0Sstevel@tonic-gate 			return (ENOSPC);
1060*0Sstevel@tonic-gate 		} else {
1061*0Sstevel@tonic-gate 			(void) strncpy(buf, unum, buflen);
1062*0Sstevel@tonic-gate 			*lenp = strlen(buf);
1063*0Sstevel@tonic-gate 			return (0);
1064*0Sstevel@tonic-gate 		}
1065*0Sstevel@tonic-gate 	}	/* end of while loop for logic bank list */
1066*0Sstevel@tonic-gate 
1067*0Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
1068*0Sstevel@tonic-gate 	return (ENXIO);
1069*0Sstevel@tonic-gate }
1070*0Sstevel@tonic-gate 
1071*0Sstevel@tonic-gate static int
1072*0Sstevel@tonic-gate mc_get_mem_info(int synd_code, uint64_t paddr,
1073*0Sstevel@tonic-gate     uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep,
1074*0Sstevel@tonic-gate     int *segsp, int *banksp, int *mcidp)
1075*0Sstevel@tonic-gate {
1076*0Sstevel@tonic-gate 	int upper_pa, lower_pa;
1077*0Sstevel@tonic-gate 	struct bank_info *bankp;
1078*0Sstevel@tonic-gate 
1079*0Sstevel@tonic-gate 	if (synd_code < -1 || synd_code >= QWORD_SIZE)
1080*0Sstevel@tonic-gate 		return (EINVAL);
1081*0Sstevel@tonic-gate 
1082*0Sstevel@tonic-gate 	upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT;
1083*0Sstevel@tonic-gate 	lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT;
1084*0Sstevel@tonic-gate 
1085*0Sstevel@tonic-gate 	/*
1086*0Sstevel@tonic-gate 	 * Scan all logical banks to get one responding to the physical
1087*0Sstevel@tonic-gate 	 * address.
1088*0Sstevel@tonic-gate 	 */
1089*0Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
1090*0Sstevel@tonic-gate 	bankp = (struct bank_info *)bank_head;
1091*0Sstevel@tonic-gate 	while (bankp != NULL) {
1092*0Sstevel@tonic-gate 		struct seg_info *segp;
1093*0Sstevel@tonic-gate 		int bankid, mcid;
1094*0Sstevel@tonic-gate 
1095*0Sstevel@tonic-gate 		bankid = bankp->bank_node.id;
1096*0Sstevel@tonic-gate 		mcid = bankid / NBANKS;
1097*0Sstevel@tonic-gate 
1098*0Sstevel@tonic-gate 		/*
1099*0Sstevel@tonic-gate 		 * The Address Decoding logic decodes the different fields
1100*0Sstevel@tonic-gate 		 * in the Memory Address Decoding register to determine
1101*0Sstevel@tonic-gate 		 * whether a particular logic bank should respond to a
1102*0Sstevel@tonic-gate 		 * physical address.
1103*0Sstevel@tonic-gate 		 */
1104*0Sstevel@tonic-gate 		if ((!bankp->valid) || ((~(~(upper_pa ^ bankp->um) |
1105*0Sstevel@tonic-gate 		    bankp->uk)) || (~(~(lower_pa ^ bankp->lm) | bankp->lk)))) {
1106*0Sstevel@tonic-gate 			bankp = (struct bank_info *)bankp->bank_node.next;
1107*0Sstevel@tonic-gate 			continue;
1108*0Sstevel@tonic-gate 		}
1109*0Sstevel@tonic-gate 
1110*0Sstevel@tonic-gate 		/*
1111*0Sstevel@tonic-gate 		 * Get the corresponding segment.
1112*0Sstevel@tonic-gate 		 */
1113*0Sstevel@tonic-gate 		if ((segp = (struct seg_info *)mc_node_get(bankp->seg_id,
1114*0Sstevel@tonic-gate 		    seg_head)) == NULL) {
1115*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
1116*0Sstevel@tonic-gate 			return (EFAULT);
1117*0Sstevel@tonic-gate 		}
1118*0Sstevel@tonic-gate 
1119*0Sstevel@tonic-gate 		*mem_sizep = memsize;
1120*0Sstevel@tonic-gate 		*seg_sizep = segp->size;
1121*0Sstevel@tonic-gate 		*bank_sizep = bankp->size;
1122*0Sstevel@tonic-gate 		*segsp = nsegments;
1123*0Sstevel@tonic-gate 		*banksp = segp->nbanks;
1124*0Sstevel@tonic-gate 		*mcidp = mcid;
1125*0Sstevel@tonic-gate 
1126*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
1127*0Sstevel@tonic-gate 
1128*0Sstevel@tonic-gate 		return (0);
1129*0Sstevel@tonic-gate 
1130*0Sstevel@tonic-gate 	}	/* end of while loop for logic bank list */
1131*0Sstevel@tonic-gate 
1132*0Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
1133*0Sstevel@tonic-gate 	return (ENXIO);
1134*0Sstevel@tonic-gate }
1135*0Sstevel@tonic-gate 
1136*0Sstevel@tonic-gate /*
1137*0Sstevel@tonic-gate  * Construct lists for an enabled MC where size of memory is 0.
1138*0Sstevel@tonic-gate  * The lists are connected as follows:
1139*0Sstevel@tonic-gate  * Attached MC -> device group list -> device list(per devgrp).
1140*0Sstevel@tonic-gate  */
1141*0Sstevel@tonic-gate static void
1142*0Sstevel@tonic-gate mc_construct(int mc_id, void *dimminfop)
1143*0Sstevel@tonic-gate {
1144*0Sstevel@tonic-gate 	int i, j, idx, dmidx;
1145*0Sstevel@tonic-gate 	struct mctrl_info *mctrl;
1146*0Sstevel@tonic-gate 	struct dgrp_info *dgrp;
1147*0Sstevel@tonic-gate 	struct device_info *dev;
1148*0Sstevel@tonic-gate 	struct	dimm_info *dimmp = (struct  dimm_info *)dimminfop;
1149*0Sstevel@tonic-gate 
1150*0Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
1151*0Sstevel@tonic-gate 	/* allocate for mctrl_info and bank_info */
1152*0Sstevel@tonic-gate 	if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id,
1153*0Sstevel@tonic-gate 	    mctrl_head)) != NULL) {
1154*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "mc_construct: mctrl %d exists\n", mc_id);
1155*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
1156*0Sstevel@tonic-gate 		return;
1157*0Sstevel@tonic-gate 	}
1158*0Sstevel@tonic-gate 
1159*0Sstevel@tonic-gate 	mctrl = kmem_zalloc(sizeof (struct mctrl_info), KM_SLEEP);
1160*0Sstevel@tonic-gate 
1161*0Sstevel@tonic-gate 	/*
1162*0Sstevel@tonic-gate 	 * If dimminfop is NULL, the Memory Controller is disable, and
1163*0Sstevel@tonic-gate 	 * the number of device group will be zero.
1164*0Sstevel@tonic-gate 	 */
1165*0Sstevel@tonic-gate 	if (dimminfop == NULL) {
1166*0Sstevel@tonic-gate 		mctrl->mctrl_node.id = mc_id;
1167*0Sstevel@tonic-gate 		mctrl->ndevgrps = 0;
1168*0Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
1169*0Sstevel@tonic-gate 		mutex_exit(&mcdatamutex);
1170*0Sstevel@tonic-gate 		return;
1171*0Sstevel@tonic-gate 	}
1172*0Sstevel@tonic-gate 
1173*0Sstevel@tonic-gate 	/* add the entry on dgrp_info list */
1174*0Sstevel@tonic-gate 	for (i = 0; i < NDGRPS; i++) {
1175*0Sstevel@tonic-gate 		idx = mc_id * NDGRPS + i;
1176*0Sstevel@tonic-gate 		mctrl->devgrpids[i] = idx;
1177*0Sstevel@tonic-gate 		if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head))
1178*0Sstevel@tonic-gate 		    != NULL) {
1179*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "mc_construct: devgrp %d exists\n",
1180*0Sstevel@tonic-gate 			    idx);
1181*0Sstevel@tonic-gate 			continue;
1182*0Sstevel@tonic-gate 		}
1183*0Sstevel@tonic-gate 
1184*0Sstevel@tonic-gate 		dgrp = kmem_zalloc(sizeof (struct dgrp_info), KM_SLEEP);
1185*0Sstevel@tonic-gate 
1186*0Sstevel@tonic-gate 		/* add the entry on device_info list */
1187*0Sstevel@tonic-gate 		for (j = 0; j < NDIMMS; j++) {
1188*0Sstevel@tonic-gate 			dmidx = idx * NDIMMS + j;
1189*0Sstevel@tonic-gate 			dgrp->deviceids[j] = dmidx;
1190*0Sstevel@tonic-gate 			if ((dev = (struct device_info *)
1191*0Sstevel@tonic-gate 			    mc_node_get(dmidx, device_head)) != NULL) {
1192*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "mc_construct: device %d "
1193*0Sstevel@tonic-gate 				    "exists\n", dmidx);
1194*0Sstevel@tonic-gate 				continue;
1195*0Sstevel@tonic-gate 			}
1196*0Sstevel@tonic-gate 			dev = kmem_zalloc(sizeof (struct device_info),
1197*0Sstevel@tonic-gate 			    KM_SLEEP);
1198*0Sstevel@tonic-gate 			dev->dev_node.id = dmidx;
1199*0Sstevel@tonic-gate 			dev->size = 0;
1200*0Sstevel@tonic-gate 			(void) strncpy(dev->label, (char *)
1201*0Sstevel@tonic-gate 			    dimmp->label[i * NDIMMS + j], MAX_DEVLEN);
1202*0Sstevel@tonic-gate 
1203*0Sstevel@tonic-gate 			mc_node_add((mc_dlist_t *)dev, &device_head,
1204*0Sstevel@tonic-gate 			    &device_tail);
1205*0Sstevel@tonic-gate 		}	/* for loop for constructing device_info */
1206*0Sstevel@tonic-gate 
1207*0Sstevel@tonic-gate 		dgrp->dgrp_node.id = idx;
1208*0Sstevel@tonic-gate 		dgrp->ndevices = NDIMMS;
1209*0Sstevel@tonic-gate 		dgrp->size = 0;
1210*0Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)dgrp, &dgrp_head, &dgrp_tail);
1211*0Sstevel@tonic-gate 
1212*0Sstevel@tonic-gate 	}	/* end of for loop for constructing dgrp_info list */
1213*0Sstevel@tonic-gate 
1214*0Sstevel@tonic-gate 	mctrl->mctrl_node.id = mc_id;
1215*0Sstevel@tonic-gate 	mctrl->ndevgrps = NDGRPS;
1216*0Sstevel@tonic-gate 	mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
1217*0Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
1218*0Sstevel@tonic-gate }
1219*0Sstevel@tonic-gate 
1220*0Sstevel@tonic-gate /*
1221*0Sstevel@tonic-gate  * Construct lists for Memory Configuration at logical viewpoint.
1222*0Sstevel@tonic-gate  *
1223*0Sstevel@tonic-gate  * Retrieve information from Memory Address Decoding Register and set up
1224*0Sstevel@tonic-gate  * bank and segment lists. Link bank to its corresponding device group, and
1225*0Sstevel@tonic-gate  * update size of device group and devices. Also connect bank to the segment.
1226*0Sstevel@tonic-gate  *
1227*0Sstevel@tonic-gate  * Memory Address Decoding Register
1228*0Sstevel@tonic-gate  * -------------------------------------------------------------------------
1229*0Sstevel@tonic-gate  * |63|62    53|52      41|40  37|36     20|19 18|17  14|13 12|11  8|7     0|
1230*0Sstevel@tonic-gate  * |-----------|----------|------|---------|-----|------|-----|-----|-------|
1231*0Sstevel@tonic-gate  * |V |    -   |    UK    |   -  |    UM   |  -  |  LK  |  -  | LM  |   -   |
1232*0Sstevel@tonic-gate  * -------------------------------------------------------------------------
1233*0Sstevel@tonic-gate  *
1234*0Sstevel@tonic-gate  */
1235*0Sstevel@tonic-gate 
1236*0Sstevel@tonic-gate static int
1237*0Sstevel@tonic-gate mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop)
1238*0Sstevel@tonic-gate {
1239*0Sstevel@tonic-gate 	int i, dmidx, idx;
1240*0Sstevel@tonic-gate 	uint32_t ifactor;
1241*0Sstevel@tonic-gate 	int status = 0;
1242*0Sstevel@tonic-gate 	uint64_t size, base;
1243*0Sstevel@tonic-gate 	struct seg_info *seg_curr;
1244*0Sstevel@tonic-gate 	struct bank_info *bank_curr;
1245*0Sstevel@tonic-gate 	struct dgrp_info *dgrp;
1246*0Sstevel@tonic-gate 	struct device_info *dev;
1247*0Sstevel@tonic-gate 	union {
1248*0Sstevel@tonic-gate 		struct {
1249*0Sstevel@tonic-gate 			uint64_t valid	: 1;
1250*0Sstevel@tonic-gate 			uint64_t resrv1	: 10;
1251*0Sstevel@tonic-gate 			uint64_t uk	: 12;
1252*0Sstevel@tonic-gate 			uint64_t resrv2	: 4;
1253*0Sstevel@tonic-gate 			uint64_t um	: 17;
1254*0Sstevel@tonic-gate 			uint64_t resrv3	: 2;
1255*0Sstevel@tonic-gate 			uint64_t lk	: 4;
1256*0Sstevel@tonic-gate 			uint64_t resrv4	: 2;
1257*0Sstevel@tonic-gate 			uint64_t lm	: 4;
1258*0Sstevel@tonic-gate 			uint64_t resrv5	: 8;
1259*0Sstevel@tonic-gate 		} _s;
1260*0Sstevel@tonic-gate 		uint64_t madreg;
1261*0Sstevel@tonic-gate 	} mcreg;
1262*0Sstevel@tonic-gate 
1263*0Sstevel@tonic-gate 	mcreg.madreg = reg;
1264*0Sstevel@tonic-gate 
1265*0Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: mc_id %d, bank num "
1266*0Sstevel@tonic-gate 	    "%d, reg 0x%llx\n", mc_id, bank_no, reg));
1267*0Sstevel@tonic-gate 
1268*0Sstevel@tonic-gate 	/* add the entry on bank_info list */
1269*0Sstevel@tonic-gate 	idx = mc_id * NBANKS + bank_no;
1270*0Sstevel@tonic-gate 
1271*0Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
1272*0Sstevel@tonic-gate 	if ((bank_curr = (struct bank_info *)mc_node_get(idx, bank_head))
1273*0Sstevel@tonic-gate 	    != NULL) {
1274*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "mlayout_add: bank %d exists\n", bank_no);
1275*0Sstevel@tonic-gate 		goto exit;
1276*0Sstevel@tonic-gate 	}
1277*0Sstevel@tonic-gate 
1278*0Sstevel@tonic-gate 	bank_curr = kmem_zalloc(sizeof (struct bank_info), KM_SLEEP);
1279*0Sstevel@tonic-gate 	bank_curr->bank_node.id = idx;
1280*0Sstevel@tonic-gate 	bank_curr->valid = mcreg._s.valid;
1281*0Sstevel@tonic-gate 	bank_curr->dimminfop = dimminfop;
1282*0Sstevel@tonic-gate 
1283*0Sstevel@tonic-gate 	if (!mcreg._s.valid) {
1284*0Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
1285*0Sstevel@tonic-gate 		goto exit;
1286*0Sstevel@tonic-gate 	}
1287*0Sstevel@tonic-gate 
1288*0Sstevel@tonic-gate 	/*
1289*0Sstevel@tonic-gate 	 * size of a logical bank = size of segment / interleave factor
1290*0Sstevel@tonic-gate 	 * This fomula is not only working for regular configuration,
1291*0Sstevel@tonic-gate 	 * i.e. number of banks at a segment equals to the max
1292*0Sstevel@tonic-gate 	 * interleave factor, but also for special case, say 3 bank
1293*0Sstevel@tonic-gate 	 * interleave. One bank is 2 way interleave and other two are
1294*0Sstevel@tonic-gate 	 * 4 way. So the sizes of banks are size of segment/2 and /4
1295*0Sstevel@tonic-gate 	 * respectively.
1296*0Sstevel@tonic-gate 	 */
1297*0Sstevel@tonic-gate 	ifactor = (mcreg._s.lk ^ 0xF) + 1;
1298*0Sstevel@tonic-gate 	size = (((mcreg._s.uk & 0x3FF) + 1) * 0x4000000) / ifactor;
1299*0Sstevel@tonic-gate 	base = mcreg._s.um & ~mcreg._s.uk;
1300*0Sstevel@tonic-gate 	base <<= MADR_UPA_SHIFT;
1301*0Sstevel@tonic-gate 
1302*0Sstevel@tonic-gate 	bank_curr->uk = mcreg._s.uk;
1303*0Sstevel@tonic-gate 	bank_curr->um = mcreg._s.um;
1304*0Sstevel@tonic-gate 	bank_curr->lk = mcreg._s.lk;
1305*0Sstevel@tonic-gate 	bank_curr->lm = mcreg._s.lm;
1306*0Sstevel@tonic-gate 	bank_curr->size = size;
1307*0Sstevel@tonic-gate 
1308*0Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add 3: logical bank num %d, "
1309*0Sstevel@tonic-gate 	"lk 0x%x uk 0x%x um 0x%x ifactor 0x%x size 0x%llx base 0x%llx\n",
1310*0Sstevel@tonic-gate 	    mcreg._s.lk, mcreg._s.uk, mcreg._s.um, ifactor, size, base));
1311*0Sstevel@tonic-gate 
1312*0Sstevel@tonic-gate 	/* connect the entry and update the size on dgrp_info list */
1313*0Sstevel@tonic-gate 	idx = mc_id * NDGRPS + (bank_no % NDGRPS);
1314*0Sstevel@tonic-gate 	if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head)) == NULL) {
1315*0Sstevel@tonic-gate 		/* all avaiable dgrp should be linked at mc_construct */
1316*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "mlayout_add: dgrp %d doesn't exist\n", idx);
1317*0Sstevel@tonic-gate 		kmem_free(bank_curr, sizeof (struct bank_info));
1318*0Sstevel@tonic-gate 		status = -1;
1319*0Sstevel@tonic-gate 		goto exit;
1320*0Sstevel@tonic-gate 	}
1321*0Sstevel@tonic-gate 
1322*0Sstevel@tonic-gate 	bank_curr->devgrp_id = idx;
1323*0Sstevel@tonic-gate 	dgrp->size += size;
1324*0Sstevel@tonic-gate 
1325*0Sstevel@tonic-gate 	/* Update the size of entry on device_info list */
1326*0Sstevel@tonic-gate 	for (i = 0; i < NDIMMS; i++) {
1327*0Sstevel@tonic-gate 		dmidx = dgrp->dgrp_node.id * NDIMMS + i;
1328*0Sstevel@tonic-gate 		dgrp->deviceids[i] = dmidx;
1329*0Sstevel@tonic-gate 
1330*0Sstevel@tonic-gate 		/* avaiable device should be linked at mc_construct */
1331*0Sstevel@tonic-gate 		if ((dev = (struct device_info *)mc_node_get(dmidx,
1332*0Sstevel@tonic-gate 		    device_head)) == NULL) {
1333*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_add:dev %d doesn't exist\n",
1334*0Sstevel@tonic-gate 			    dmidx);
1335*0Sstevel@tonic-gate 			kmem_free(bank_curr, sizeof (struct bank_info));
1336*0Sstevel@tonic-gate 			status = -1;
1337*0Sstevel@tonic-gate 			goto exit;
1338*0Sstevel@tonic-gate 		}
1339*0Sstevel@tonic-gate 
1340*0Sstevel@tonic-gate 		dev->size += (size / NDIMMS);
1341*0Sstevel@tonic-gate 
1342*0Sstevel@tonic-gate 		DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add DIMM:id %d, size %d\n",
1343*0Sstevel@tonic-gate 		    dmidx, size));
1344*0Sstevel@tonic-gate 	}
1345*0Sstevel@tonic-gate 
1346*0Sstevel@tonic-gate 	/*
1347*0Sstevel@tonic-gate 	 * Get the segment by matching the base address, link this bank
1348*0Sstevel@tonic-gate 	 * to the segment. If not matched, allocate a new segment and
1349*0Sstevel@tonic-gate 	 * add it at segment list.
1350*0Sstevel@tonic-gate 	 */
1351*0Sstevel@tonic-gate 	if (seg_curr = seg_match_base(base)) {
1352*0Sstevel@tonic-gate 		seg_curr->nbanks++;
1353*0Sstevel@tonic-gate 		seg_curr->size += size;
1354*0Sstevel@tonic-gate 		if (ifactor > seg_curr->ifactor)
1355*0Sstevel@tonic-gate 			seg_curr->ifactor = ifactor;
1356*0Sstevel@tonic-gate 		bank_curr->seg_id = seg_curr->seg_node.id;
1357*0Sstevel@tonic-gate 	} else {
1358*0Sstevel@tonic-gate 		seg_curr = (struct seg_info *)
1359*0Sstevel@tonic-gate 		kmem_zalloc(sizeof (struct seg_info), KM_SLEEP);
1360*0Sstevel@tonic-gate 		bank_curr->seg_id = seg_id;
1361*0Sstevel@tonic-gate 		seg_curr->seg_node.id = seg_id++;
1362*0Sstevel@tonic-gate 		seg_curr->base = base;
1363*0Sstevel@tonic-gate 		seg_curr->size = size;
1364*0Sstevel@tonic-gate 		seg_curr->nbanks = 1;
1365*0Sstevel@tonic-gate 		seg_curr->ifactor = ifactor;
1366*0Sstevel@tonic-gate 		mc_node_add((mc_dlist_t *)seg_curr, &seg_head, &seg_tail);
1367*0Sstevel@tonic-gate 
1368*0Sstevel@tonic-gate 		nsegments++;
1369*0Sstevel@tonic-gate 	}
1370*0Sstevel@tonic-gate 
1371*0Sstevel@tonic-gate 	/* Get the local id of bank which is only unique per segment. */
1372*0Sstevel@tonic-gate 	bank_curr->local_id = seg_curr->nbanks - 1;
1373*0Sstevel@tonic-gate 
1374*0Sstevel@tonic-gate 	/* add bank at the end of the list; not sorted by bankid */
1375*0Sstevel@tonic-gate 	if (seg_curr->hb_inseg != NULL) {
1376*0Sstevel@tonic-gate 		bank_curr->p_inseg = seg_curr->tb_inseg;
1377*0Sstevel@tonic-gate 		bank_curr->n_inseg = seg_curr->tb_inseg->n_inseg;
1378*0Sstevel@tonic-gate 		seg_curr->tb_inseg->n_inseg = bank_curr;
1379*0Sstevel@tonic-gate 		seg_curr->tb_inseg = bank_curr;
1380*0Sstevel@tonic-gate 	} else {
1381*0Sstevel@tonic-gate 		bank_curr->n_inseg = bank_curr->p_inseg = NULL;
1382*0Sstevel@tonic-gate 		seg_curr->hb_inseg = seg_curr->tb_inseg = bank_curr;
1383*0Sstevel@tonic-gate 	}
1384*0Sstevel@tonic-gate 	DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: + bank to seg, id %d\n",
1385*0Sstevel@tonic-gate 	    seg_curr->seg_node.id));
1386*0Sstevel@tonic-gate 
1387*0Sstevel@tonic-gate 	mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
1388*0Sstevel@tonic-gate 
1389*0Sstevel@tonic-gate 	memsize += size;
1390*0Sstevel@tonic-gate 	if (seg_curr->nbanks > maxbanks)
1391*0Sstevel@tonic-gate 		maxbanks = seg_curr->nbanks;
1392*0Sstevel@tonic-gate 
1393*0Sstevel@tonic-gate exit:
1394*0Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
1395*0Sstevel@tonic-gate 	return (status);
1396*0Sstevel@tonic-gate }
1397*0Sstevel@tonic-gate 
1398*0Sstevel@tonic-gate /*
1399*0Sstevel@tonic-gate  * Delete nodes related to the given MC on mc, device group, device,
1400*0Sstevel@tonic-gate  * and bank lists. Moreover, delete corresponding segment if its connected
1401*0Sstevel@tonic-gate  * banks are all removed.
1402*0Sstevel@tonic-gate  */
1403*0Sstevel@tonic-gate static void
1404*0Sstevel@tonic-gate mlayout_del(int mc_id)
1405*0Sstevel@tonic-gate {
1406*0Sstevel@tonic-gate 	int i, j, dgrpid, devid, bankid, ndevgrps;
1407*0Sstevel@tonic-gate 	struct seg_info *seg;
1408*0Sstevel@tonic-gate 	struct bank_info *bank_curr;
1409*0Sstevel@tonic-gate 	struct mctrl_info *mctrl;
1410*0Sstevel@tonic-gate 	mc_dlist_t *dgrp_ptr;
1411*0Sstevel@tonic-gate 	mc_dlist_t *dev_ptr;
1412*0Sstevel@tonic-gate 	uint64_t base;
1413*0Sstevel@tonic-gate 
1414*0Sstevel@tonic-gate 	mutex_enter(&mcdatamutex);
1415*0Sstevel@tonic-gate 
1416*0Sstevel@tonic-gate 	/* delete mctrl_info */
1417*0Sstevel@tonic-gate 	if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id, mctrl_head)) !=
1418*0Sstevel@tonic-gate 	    NULL) {
1419*0Sstevel@tonic-gate 		ndevgrps = mctrl->ndevgrps;
1420*0Sstevel@tonic-gate 		mc_node_del((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail);
1421*0Sstevel@tonic-gate 		kmem_free(mctrl, sizeof (struct mctrl_info));
1422*0Sstevel@tonic-gate 		nmcs--;
1423*0Sstevel@tonic-gate 
1424*0Sstevel@tonic-gate 		/*
1425*0Sstevel@tonic-gate 		 * There is no other list left for disabled MC.
1426*0Sstevel@tonic-gate 		 */
1427*0Sstevel@tonic-gate 		if (ndevgrps == 0) {
1428*0Sstevel@tonic-gate 			mutex_exit(&mcdatamutex);
1429*0Sstevel@tonic-gate 			return;
1430*0Sstevel@tonic-gate 		}
1431*0Sstevel@tonic-gate 	} else
1432*0Sstevel@tonic-gate 		cmn_err(CE_WARN, "MC mlayout_del: mctrl is not found\n");
1433*0Sstevel@tonic-gate 
1434*0Sstevel@tonic-gate 	/* Delete device groups and devices of the detached MC */
1435*0Sstevel@tonic-gate 	for (i = 0; i < NDGRPS; i++) {
1436*0Sstevel@tonic-gate 		dgrpid = mc_id * NDGRPS + i;
1437*0Sstevel@tonic-gate 		if (!(dgrp_ptr = mc_node_get(dgrpid, dgrp_head))) {
1438*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_del: no devgrp %d\n", dgrpid);
1439*0Sstevel@tonic-gate 			continue;
1440*0Sstevel@tonic-gate 		}
1441*0Sstevel@tonic-gate 
1442*0Sstevel@tonic-gate 		for (j = 0; j < NDIMMS; j++) {
1443*0Sstevel@tonic-gate 			devid = dgrpid * NDIMMS + j;
1444*0Sstevel@tonic-gate 			if (dev_ptr = mc_node_get(devid, device_head)) {
1445*0Sstevel@tonic-gate 				mc_node_del(dev_ptr, &device_head,
1446*0Sstevel@tonic-gate 				    &device_tail);
1447*0Sstevel@tonic-gate 				kmem_free(dev_ptr, sizeof (struct device_info));
1448*0Sstevel@tonic-gate 			} else {
1449*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "mlayout_del: no dev %d\n",
1450*0Sstevel@tonic-gate 				    devid);
1451*0Sstevel@tonic-gate 			}
1452*0Sstevel@tonic-gate 		}
1453*0Sstevel@tonic-gate 
1454*0Sstevel@tonic-gate 		mc_node_del(dgrp_ptr, &dgrp_head, &dgrp_tail);
1455*0Sstevel@tonic-gate 		kmem_free(dgrp_ptr, sizeof (struct dgrp_info));
1456*0Sstevel@tonic-gate 	}
1457*0Sstevel@tonic-gate 
1458*0Sstevel@tonic-gate 	/* Delete banks and segments if it has no bank */
1459*0Sstevel@tonic-gate 	for (i = 0; i < NBANKS; i++) {
1460*0Sstevel@tonic-gate 		bankid = mc_id * NBANKS + i;
1461*0Sstevel@tonic-gate 		DPRINTF(MC_DESTRC_DEBUG, ("bank id %d\n", bankid));
1462*0Sstevel@tonic-gate 		if (!(bank_curr = (struct bank_info *)mc_node_get(bankid,
1463*0Sstevel@tonic-gate 		    bank_head))) {
1464*0Sstevel@tonic-gate 			cmn_err(CE_WARN, "mlayout_del: no bank %d\n", bankid);
1465*0Sstevel@tonic-gate 			continue;
1466*0Sstevel@tonic-gate 		}
1467*0Sstevel@tonic-gate 
1468*0Sstevel@tonic-gate 		if (bank_curr->valid) {
1469*0Sstevel@tonic-gate 			base = bank_curr->um & ~bank_curr->uk;
1470*0Sstevel@tonic-gate 			base <<= MADR_UPA_SHIFT;
1471*0Sstevel@tonic-gate 			bank_curr->valid = 0;
1472*0Sstevel@tonic-gate 			memsize -= bank_curr->size;
1473*0Sstevel@tonic-gate 
1474*0Sstevel@tonic-gate 			/* Delete bank at segment and segment if no bank left */
1475*0Sstevel@tonic-gate 			if (!(seg = seg_match_base(base))) {
1476*0Sstevel@tonic-gate 				cmn_err(CE_WARN, "mlayout_del: no seg\n");
1477*0Sstevel@tonic-gate 				mc_node_del((mc_dlist_t *)bank_curr, &bank_head,
1478*0Sstevel@tonic-gate 				    &bank_tail);
1479*0Sstevel@tonic-gate 				kmem_free(bank_curr, sizeof (struct bank_info));
1480*0Sstevel@tonic-gate 				continue;
1481*0Sstevel@tonic-gate 			}
1482*0Sstevel@tonic-gate 
1483*0Sstevel@tonic-gate 			/* update the bank list at the segment */
1484*0Sstevel@tonic-gate 			if (bank_curr->n_inseg == NULL) {
1485*0Sstevel@tonic-gate 				/* node is at the tail of list */
1486*0Sstevel@tonic-gate 				seg->tb_inseg = bank_curr->p_inseg;
1487*0Sstevel@tonic-gate 			} else {
1488*0Sstevel@tonic-gate 				bank_curr->n_inseg->p_inseg =
1489*0Sstevel@tonic-gate 				    bank_curr->p_inseg;
1490*0Sstevel@tonic-gate 			}
1491*0Sstevel@tonic-gate 
1492*0Sstevel@tonic-gate 			if (bank_curr->p_inseg == NULL) {
1493*0Sstevel@tonic-gate 				/* node is at the head of list */
1494*0Sstevel@tonic-gate 				seg->hb_inseg = bank_curr->n_inseg;
1495*0Sstevel@tonic-gate 			} else {
1496*0Sstevel@tonic-gate 				bank_curr->p_inseg->n_inseg =
1497*0Sstevel@tonic-gate 				    bank_curr->n_inseg;
1498*0Sstevel@tonic-gate 			}
1499*0Sstevel@tonic-gate 
1500*0Sstevel@tonic-gate 			seg->nbanks--;
1501*0Sstevel@tonic-gate 			seg->size -= bank_curr->size;
1502*0Sstevel@tonic-gate 
1503*0Sstevel@tonic-gate 			if (seg->nbanks == 0) {
1504*0Sstevel@tonic-gate 				mc_node_del((mc_dlist_t *)seg, &seg_head,
1505*0Sstevel@tonic-gate 				    &seg_tail);
1506*0Sstevel@tonic-gate 				kmem_free(seg, sizeof (struct seg_info));
1507*0Sstevel@tonic-gate 				nsegments--;
1508*0Sstevel@tonic-gate 			}
1509*0Sstevel@tonic-gate 
1510*0Sstevel@tonic-gate 		}
1511*0Sstevel@tonic-gate 		mc_node_del((mc_dlist_t *)bank_curr, &bank_head, &bank_tail);
1512*0Sstevel@tonic-gate 		kmem_free(bank_curr, sizeof (struct bank_info));
1513*0Sstevel@tonic-gate 	}	/* end of for loop for four banks */
1514*0Sstevel@tonic-gate 
1515*0Sstevel@tonic-gate 	mutex_exit(&mcdatamutex);
1516*0Sstevel@tonic-gate }
1517*0Sstevel@tonic-gate 
1518*0Sstevel@tonic-gate /*
1519*0Sstevel@tonic-gate  * Search the segment in the list starting at seg_head by base address
1520*0Sstevel@tonic-gate  * input: base address
1521*0Sstevel@tonic-gate  * return: pointer of found segment or null if not found.
1522*0Sstevel@tonic-gate  */
1523*0Sstevel@tonic-gate static struct seg_info *
1524*0Sstevel@tonic-gate seg_match_base(u_longlong_t base)
1525*0Sstevel@tonic-gate {
1526*0Sstevel@tonic-gate 	static struct seg_info *seg_ptr;
1527*0Sstevel@tonic-gate 
1528*0Sstevel@tonic-gate 	seg_ptr = (struct seg_info *)seg_head;
1529*0Sstevel@tonic-gate 	while (seg_ptr != NULL) {
1530*0Sstevel@tonic-gate 		DPRINTF(MC_LIST_DEBUG, ("seg_match: base %d,given base %d\n",
1531*0Sstevel@tonic-gate 		    seg_ptr->base, base));
1532*0Sstevel@tonic-gate 		if (seg_ptr->base == base)
1533*0Sstevel@tonic-gate 			break;
1534*0Sstevel@tonic-gate 		seg_ptr = (struct seg_info *)seg_ptr->seg_node.next;
1535*0Sstevel@tonic-gate 	}
1536*0Sstevel@tonic-gate 	return (seg_ptr);
1537*0Sstevel@tonic-gate }
1538*0Sstevel@tonic-gate 
1539*0Sstevel@tonic-gate /*
1540*0Sstevel@tonic-gate  * mc_dlist is a double linking list, including unique id, and pointers to
1541*0Sstevel@tonic-gate  * next, and previous nodes. seg_info, bank_info, dgrp_info, device_info,
1542*0Sstevel@tonic-gate  * and mctrl_info has it at the top to share the operations, add, del, and get.
1543*0Sstevel@tonic-gate  *
1544*0Sstevel@tonic-gate  * The new node is added at the tail and is not sorted.
1545*0Sstevel@tonic-gate  *
1546*0Sstevel@tonic-gate  * Input: The pointer of node to be added, head and tail of the list
1547*0Sstevel@tonic-gate  */
1548*0Sstevel@tonic-gate 
1549*0Sstevel@tonic-gate static void
1550*0Sstevel@tonic-gate mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
1551*0Sstevel@tonic-gate {
1552*0Sstevel@tonic-gate 	DPRINTF(MC_LIST_DEBUG, ("mc_node_add: node->id %d head %p tail %p\n",
1553*0Sstevel@tonic-gate 		node->id, *head, *tail));
1554*0Sstevel@tonic-gate 
1555*0Sstevel@tonic-gate 	if (*head != NULL) {
1556*0Sstevel@tonic-gate 		node->prev = *tail;
1557*0Sstevel@tonic-gate 		node->next = (*tail)->next;
1558*0Sstevel@tonic-gate 		(*tail)->next = node;
1559*0Sstevel@tonic-gate 		*tail = node;
1560*0Sstevel@tonic-gate 	} else {
1561*0Sstevel@tonic-gate 		node->next = node->prev = NULL;
1562*0Sstevel@tonic-gate 		*head = *tail = node;
1563*0Sstevel@tonic-gate 	}
1564*0Sstevel@tonic-gate }
1565*0Sstevel@tonic-gate 
1566*0Sstevel@tonic-gate /*
1567*0Sstevel@tonic-gate  * Input: The pointer of node to be deleted, head and tail of the list
1568*0Sstevel@tonic-gate  *
1569*0Sstevel@tonic-gate  * Deleted node will be at the following positions
1570*0Sstevel@tonic-gate  * 1. At the tail of the list
1571*0Sstevel@tonic-gate  * 2. At the head of the list
1572*0Sstevel@tonic-gate  * 3. At the head and tail of the list, i.e. only one left.
1573*0Sstevel@tonic-gate  * 4. At the middle of the list
1574*0Sstevel@tonic-gate  */
1575*0Sstevel@tonic-gate 
1576*0Sstevel@tonic-gate static void
1577*0Sstevel@tonic-gate mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail)
1578*0Sstevel@tonic-gate {
1579*0Sstevel@tonic-gate 	if (node->next == NULL) {
1580*0Sstevel@tonic-gate 		/* deleted node is at the tail of list */
1581*0Sstevel@tonic-gate 		*tail = node->prev;
1582*0Sstevel@tonic-gate 	} else {
1583*0Sstevel@tonic-gate 		node->next->prev = node->prev;
1584*0Sstevel@tonic-gate 	}
1585*0Sstevel@tonic-gate 
1586*0Sstevel@tonic-gate 	if (node->prev == NULL) {
1587*0Sstevel@tonic-gate 		/* deleted node is at the head of list */
1588*0Sstevel@tonic-gate 		*head = node->next;
1589*0Sstevel@tonic-gate 	} else {
1590*0Sstevel@tonic-gate 		node->prev->next = node->next;
1591*0Sstevel@tonic-gate 	}
1592*0Sstevel@tonic-gate }
1593*0Sstevel@tonic-gate 
1594*0Sstevel@tonic-gate /*
1595*0Sstevel@tonic-gate  * Search the list from the head of the list to match the given id
1596*0Sstevel@tonic-gate  * Input: id and the head of the list
1597*0Sstevel@tonic-gate  * Return: pointer of found node
1598*0Sstevel@tonic-gate  */
1599*0Sstevel@tonic-gate static mc_dlist_t *
1600*0Sstevel@tonic-gate mc_node_get(int id, mc_dlist_t *head)
1601*0Sstevel@tonic-gate {
1602*0Sstevel@tonic-gate 	mc_dlist_t *node;
1603*0Sstevel@tonic-gate 
1604*0Sstevel@tonic-gate 	node = head;
1605*0Sstevel@tonic-gate 	while (node != NULL) {
1606*0Sstevel@tonic-gate 		DPRINTF(MC_LIST_DEBUG, ("mc_node_get: id %d, given id %d\n",
1607*0Sstevel@tonic-gate 		    node->id, id));
1608*0Sstevel@tonic-gate 		if (node->id == id)
1609*0Sstevel@tonic-gate 			break;
1610*0Sstevel@tonic-gate 		node = node->next;
1611*0Sstevel@tonic-gate 	}
1612*0Sstevel@tonic-gate 	return (node);
1613*0Sstevel@tonic-gate }
1614*0Sstevel@tonic-gate 
1615*0Sstevel@tonic-gate /*
1616*0Sstevel@tonic-gate  * mc-us3 driver allows a platform to add extra label
1617*0Sstevel@tonic-gate  * information to the unum string. If a platform implements a
1618*0Sstevel@tonic-gate  * kernel function called plat_add_mem_unum_label() it will be
1619*0Sstevel@tonic-gate  * executed. This would typically be implemented in the platmod.
1620*0Sstevel@tonic-gate  */
1621*0Sstevel@tonic-gate static void
1622*0Sstevel@tonic-gate mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm)
1623*0Sstevel@tonic-gate {
1624*0Sstevel@tonic-gate 	if (&plat_add_mem_unum_label)
1625*0Sstevel@tonic-gate 		plat_add_mem_unum_label(buf, mcid, bank, dimm);
1626*0Sstevel@tonic-gate }
1627