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