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