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