10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*1463Sayznaga * Common Development and Distribution License (the "License"). 6*1463Sayznaga * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*1463Sayznaga * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include <sys/types.h> 290Sstevel@tonic-gate #include <sys/conf.h> 300Sstevel@tonic-gate #include <sys/ddi.h> 310Sstevel@tonic-gate #include <sys/stat.h> 320Sstevel@tonic-gate #include <sys/sunddi.h> 330Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 340Sstevel@tonic-gate #include <sys/obpdefs.h> 350Sstevel@tonic-gate #include <sys/cmn_err.h> 360Sstevel@tonic-gate #include <sys/errno.h> 370Sstevel@tonic-gate #include <sys/kmem.h> 380Sstevel@tonic-gate #include <sys/open.h> 390Sstevel@tonic-gate #include <sys/thread.h> 400Sstevel@tonic-gate #include <sys/cpuvar.h> 410Sstevel@tonic-gate #include <sys/x_call.h> 420Sstevel@tonic-gate #include <sys/debug.h> 430Sstevel@tonic-gate #include <sys/sysmacros.h> 440Sstevel@tonic-gate #include <sys/ivintr.h> 450Sstevel@tonic-gate #include <sys/intr.h> 460Sstevel@tonic-gate #include <sys/intreg.h> 470Sstevel@tonic-gate #include <sys/autoconf.h> 480Sstevel@tonic-gate #include <sys/modctl.h> 490Sstevel@tonic-gate #include <sys/spl.h> 500Sstevel@tonic-gate #include <sys/async.h> 510Sstevel@tonic-gate #include <sys/mc.h> 520Sstevel@tonic-gate #include <sys/mc-us3.h> 530Sstevel@tonic-gate #include <sys/cpu_module.h> 541186Sayznaga #include <sys/platform_module.h> 550Sstevel@tonic-gate 560Sstevel@tonic-gate /* 570Sstevel@tonic-gate * Function prototypes 580Sstevel@tonic-gate */ 590Sstevel@tonic-gate 600Sstevel@tonic-gate static int mc_open(dev_t *, int, int, cred_t *); 610Sstevel@tonic-gate static int mc_close(dev_t, int, int, cred_t *); 620Sstevel@tonic-gate static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 630Sstevel@tonic-gate static int mc_attach(dev_info_t *, ddi_attach_cmd_t); 640Sstevel@tonic-gate static int mc_detach(dev_info_t *, ddi_detach_cmd_t); 650Sstevel@tonic-gate 660Sstevel@tonic-gate /* 670Sstevel@tonic-gate * Configuration data structures 680Sstevel@tonic-gate */ 690Sstevel@tonic-gate static struct cb_ops mc_cb_ops = { 700Sstevel@tonic-gate mc_open, /* open */ 710Sstevel@tonic-gate mc_close, /* close */ 720Sstevel@tonic-gate nulldev, /* strategy */ 730Sstevel@tonic-gate nulldev, /* print */ 740Sstevel@tonic-gate nodev, /* dump */ 750Sstevel@tonic-gate nulldev, /* read */ 760Sstevel@tonic-gate nulldev, /* write */ 770Sstevel@tonic-gate mc_ioctl, /* ioctl */ 780Sstevel@tonic-gate nodev, /* devmap */ 790Sstevel@tonic-gate nodev, /* mmap */ 800Sstevel@tonic-gate nodev, /* segmap */ 810Sstevel@tonic-gate nochpoll, /* poll */ 820Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 830Sstevel@tonic-gate 0, /* streamtab */ 840Sstevel@tonic-gate D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 850Sstevel@tonic-gate CB_REV, /* rev */ 860Sstevel@tonic-gate nodev, /* cb_aread */ 870Sstevel@tonic-gate nodev /* cb_awrite */ 880Sstevel@tonic-gate }; 890Sstevel@tonic-gate 900Sstevel@tonic-gate static struct dev_ops mc_ops = { 910Sstevel@tonic-gate DEVO_REV, /* rev */ 920Sstevel@tonic-gate 0, /* refcnt */ 930Sstevel@tonic-gate ddi_getinfo_1to1, /* getinfo */ 940Sstevel@tonic-gate nulldev, /* identify */ 950Sstevel@tonic-gate nulldev, /* probe */ 960Sstevel@tonic-gate mc_attach, /* attach */ 970Sstevel@tonic-gate mc_detach, /* detach */ 980Sstevel@tonic-gate nulldev, /* reset */ 990Sstevel@tonic-gate &mc_cb_ops, /* cb_ops */ 1000Sstevel@tonic-gate (struct bus_ops *)0, /* bus_ops */ 1010Sstevel@tonic-gate nulldev /* power */ 1020Sstevel@tonic-gate }; 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate /* 1050Sstevel@tonic-gate * Driver globals 1060Sstevel@tonic-gate */ 1070Sstevel@tonic-gate static void *mcp; 1080Sstevel@tonic-gate static int nmcs = 0; 1090Sstevel@tonic-gate static int seg_id = 0; 1100Sstevel@tonic-gate static int nsegments = 0; 1110Sstevel@tonic-gate static uint64_t memsize = 0; 1120Sstevel@tonic-gate static int maxbanks = 0; 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate static mc_dlist_t *seg_head, *seg_tail, *bank_head, *bank_tail; 1150Sstevel@tonic-gate static mc_dlist_t *mctrl_head, *mctrl_tail, *dgrp_head, *dgrp_tail; 1160Sstevel@tonic-gate static mc_dlist_t *device_head, *device_tail; 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate static kmutex_t mcmutex; 1190Sstevel@tonic-gate static kmutex_t mcdatamutex; 1200Sstevel@tonic-gate static int mc_is_open = 0; 1210Sstevel@tonic-gate 1221186Sayznaga static krwlock_t mcdimmsids_rw; 1231186Sayznaga 1241186Sayznaga /* pointer to cache of DIMM serial ids */ 1251186Sayznaga static dimm_sid_cache_t *mc_dimm_sids; 1261186Sayznaga static int max_entries; 1271186Sayznaga 1280Sstevel@tonic-gate extern struct mod_ops mod_driverops; 1290Sstevel@tonic-gate 1300Sstevel@tonic-gate static struct modldrv modldrv = { 1310Sstevel@tonic-gate &mod_driverops, /* module type, this one is a driver */ 1320Sstevel@tonic-gate "Memory-controller: %I%", /* module name */ 1330Sstevel@tonic-gate &mc_ops, /* driver ops */ 1340Sstevel@tonic-gate }; 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate static struct modlinkage modlinkage = { 1370Sstevel@tonic-gate MODREV_1, /* rev */ 1380Sstevel@tonic-gate (void *)&modldrv, 1390Sstevel@tonic-gate NULL 1400Sstevel@tonic-gate }; 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate static int mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf, 1430Sstevel@tonic-gate int buflen, int *lenp); 1440Sstevel@tonic-gate static int mc_get_mem_info(int synd_code, uint64_t paddr, 1450Sstevel@tonic-gate uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep, 1460Sstevel@tonic-gate int *segsp, int *banksp, int *mcidp); 1471186Sayznaga static int mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp); 1481186Sayznaga static int mc_get_mem_offset(uint64_t paddr, uint64_t *offp); 1491186Sayznaga static int mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr); 1501186Sayznaga static int mc_init_sid_cache(void); 1510Sstevel@tonic-gate static int mc_get_mcregs(struct mc_soft_state *); 1520Sstevel@tonic-gate static void mc_construct(int mc_id, void *dimminfop); 1530Sstevel@tonic-gate static int mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop); 1541186Sayznaga static void mlayout_del(int mc_id, int delete); 1550Sstevel@tonic-gate static struct seg_info *seg_match_base(u_longlong_t base); 1560Sstevel@tonic-gate static void mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail); 1570Sstevel@tonic-gate static void mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail); 1580Sstevel@tonic-gate static mc_dlist_t *mc_node_get(int id, mc_dlist_t *head); 1590Sstevel@tonic-gate static void mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm); 1601186Sayznaga static int mc_populate_sid_cache(void); 1611186Sayznaga static int mc_get_sid_cache_index(int mcid); 162*1463Sayznaga static void mc_update_bank(struct bank_info *bank); 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate #pragma weak p2get_mem_unum 1650Sstevel@tonic-gate #pragma weak p2get_mem_info 1661186Sayznaga #pragma weak p2get_mem_sid 1671186Sayznaga #pragma weak p2get_mem_offset 1681186Sayznaga #pragma weak p2get_mem_addr 1691186Sayznaga #pragma weak p2init_sid_cache 1700Sstevel@tonic-gate #pragma weak plat_add_mem_unum_label 1711186Sayznaga #pragma weak plat_alloc_sid_cache 1721186Sayznaga #pragma weak plat_populate_sid_cache 1731186Sayznaga 1741186Sayznaga #define QWORD_SIZE 144 1751186Sayznaga #define QWORD_SIZE_BYTES (QWORD_SIZE / 8) 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate /* 1780Sstevel@tonic-gate * These are the module initialization routines. 1790Sstevel@tonic-gate */ 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate int 1820Sstevel@tonic-gate _init(void) 1830Sstevel@tonic-gate { 1840Sstevel@tonic-gate int error; 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate if ((error = ddi_soft_state_init(&mcp, 1870Sstevel@tonic-gate sizeof (struct mc_soft_state), 1)) != 0) 1880Sstevel@tonic-gate return (error); 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate error = mod_install(&modlinkage); 1910Sstevel@tonic-gate if (error == 0) { 1920Sstevel@tonic-gate mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL); 1930Sstevel@tonic-gate mutex_init(&mcdatamutex, NULL, MUTEX_DRIVER, NULL); 1941186Sayznaga rw_init(&mcdimmsids_rw, NULL, RW_DRIVER, NULL); 1950Sstevel@tonic-gate } 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate return (error); 1980Sstevel@tonic-gate } 1990Sstevel@tonic-gate 2000Sstevel@tonic-gate int 2010Sstevel@tonic-gate _fini(void) 2020Sstevel@tonic-gate { 2030Sstevel@tonic-gate int error; 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate if ((error = mod_remove(&modlinkage)) != 0) 2060Sstevel@tonic-gate return (error); 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate ddi_soft_state_fini(&mcp); 2090Sstevel@tonic-gate mutex_destroy(&mcmutex); 2100Sstevel@tonic-gate mutex_destroy(&mcdatamutex); 2111186Sayznaga rw_destroy(&mcdimmsids_rw); 2121186Sayznaga 2131186Sayznaga if (mc_dimm_sids) 2141186Sayznaga kmem_free(mc_dimm_sids, sizeof (dimm_sid_cache_t) * 2151186Sayznaga max_entries); 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate return (0); 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate int 2210Sstevel@tonic-gate _info(struct modinfo *modinfop) 2220Sstevel@tonic-gate { 2230Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 2240Sstevel@tonic-gate } 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate static int 2270Sstevel@tonic-gate mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 2280Sstevel@tonic-gate { 2290Sstevel@tonic-gate struct mc_soft_state *softsp; 2300Sstevel@tonic-gate struct dimm_info *dimminfop; 2310Sstevel@tonic-gate int instance, len, err; 2320Sstevel@tonic-gate 2330Sstevel@tonic-gate /* get the instance of this devi */ 2340Sstevel@tonic-gate instance = ddi_get_instance(devi); 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate switch (cmd) { 2370Sstevel@tonic-gate case DDI_ATTACH: 2380Sstevel@tonic-gate break; 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate case DDI_RESUME: 2410Sstevel@tonic-gate /* get the soft state pointer for this device node */ 2420Sstevel@tonic-gate softsp = ddi_get_soft_state(mcp, instance); 2430Sstevel@tonic-gate DPRINTF(MC_ATTACH_DEBUG, ("mc%d: DDI_RESUME: updating MADRs\n", 2440Sstevel@tonic-gate instance)); 2450Sstevel@tonic-gate /* 2460Sstevel@tonic-gate * During resume, the source and target board's bank_infos 2470Sstevel@tonic-gate * need to be updated with the new mc MADR values. This is 2480Sstevel@tonic-gate * implemented with existing functionality by first removing 2490Sstevel@tonic-gate * the props and allocated data structs, and then adding them 2500Sstevel@tonic-gate * back in. 2510Sstevel@tonic-gate */ 2520Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip, 2530Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 2540Sstevel@tonic-gate MEM_CFG_PROP_NAME) == 1) { 2550Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip, 2560Sstevel@tonic-gate MEM_CFG_PROP_NAME); 2570Sstevel@tonic-gate } 2581186Sayznaga mlayout_del(softsp->portid, 0); 2590Sstevel@tonic-gate if (mc_get_mcregs(softsp) == -1) { 2600Sstevel@tonic-gate cmn_err(CE_WARN, "mc_attach: mc%d DDI_RESUME failure\n", 2610Sstevel@tonic-gate instance); 2620Sstevel@tonic-gate } 2630Sstevel@tonic-gate return (DDI_SUCCESS); 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate default: 2660Sstevel@tonic-gate return (DDI_FAILURE); 2670Sstevel@tonic-gate } 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate if (ddi_soft_state_zalloc(mcp, instance) != DDI_SUCCESS) 2700Sstevel@tonic-gate return (DDI_FAILURE); 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate softsp = ddi_get_soft_state(mcp, instance); 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate /* Set the dip in the soft state */ 2750Sstevel@tonic-gate softsp->dip = devi; 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate if ((softsp->portid = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip, 2780Sstevel@tonic-gate DDI_PROP_DONTPASS, "portid", -1)) == -1) { 2790Sstevel@tonic-gate DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to get %s property", 2800Sstevel@tonic-gate instance, "portid")); 2810Sstevel@tonic-gate goto bad; 2820Sstevel@tonic-gate } 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate DPRINTF(MC_ATTACH_DEBUG, ("mc%d ATTACH: portid %d, cpuid %d\n", 2850Sstevel@tonic-gate instance, softsp->portid, CPU->cpu_id)); 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate /* map in the registers for this device. */ 2880Sstevel@tonic-gate if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0)) { 2890Sstevel@tonic-gate DPRINTF(MC_ATTACH_DEBUG, ("mc%d: unable to map registers", 2900Sstevel@tonic-gate instance)); 2910Sstevel@tonic-gate goto bad; 2920Sstevel@tonic-gate } 2930Sstevel@tonic-gate 2940Sstevel@tonic-gate /* 2950Sstevel@tonic-gate * Get the label of dimms and pin routing information at memory-layout 2960Sstevel@tonic-gate * property if the memory controller is enabled. 2970Sstevel@tonic-gate * 2980Sstevel@tonic-gate * Basically every memory-controller node on every machine should 2990Sstevel@tonic-gate * have one of these properties unless the memory controller is 3000Sstevel@tonic-gate * physically not capable of having memory attached to it, e.g. 3010Sstevel@tonic-gate * Excalibur's slave processor. 3020Sstevel@tonic-gate */ 3030Sstevel@tonic-gate err = ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip, DDI_PROP_DONTPASS, 3040Sstevel@tonic-gate "memory-layout", (caddr_t)&dimminfop, &len); 3050Sstevel@tonic-gate if (err == DDI_PROP_SUCCESS) { 3060Sstevel@tonic-gate /* 3070Sstevel@tonic-gate * Set the pointer and size of property in the soft state 3080Sstevel@tonic-gate */ 3090Sstevel@tonic-gate softsp->memlayoutp = dimminfop; 3100Sstevel@tonic-gate softsp->size = len; 3110Sstevel@tonic-gate } else if (err == DDI_PROP_NOT_FOUND) { 3120Sstevel@tonic-gate /* 3130Sstevel@tonic-gate * This is a disable MC. Clear out the pointer and size 3140Sstevel@tonic-gate * of property in the soft state 3150Sstevel@tonic-gate */ 3160Sstevel@tonic-gate softsp->memlayoutp = NULL; 3170Sstevel@tonic-gate softsp->size = 0; 3180Sstevel@tonic-gate } else { 3190Sstevel@tonic-gate DPRINTF(MC_ATTACH_DEBUG, ("mc%d is disabled: dimminfop %p\n", 3200Sstevel@tonic-gate instance, dimminfop)); 3210Sstevel@tonic-gate goto bad2; 3220Sstevel@tonic-gate } 3230Sstevel@tonic-gate 324946Smathue DPRINTF(MC_ATTACH_DEBUG, ("mc%d: dimminfop=0x%p data=0x%lx len=%d\n", 325946Smathue instance, dimminfop, *(uint64_t *)dimminfop, len)); 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate /* Get MC registers and construct all needed data structure */ 3280Sstevel@tonic-gate if (mc_get_mcregs(softsp) == -1) 3290Sstevel@tonic-gate goto bad1; 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate mutex_enter(&mcmutex); 3320Sstevel@tonic-gate if (nmcs == 1) { 3330Sstevel@tonic-gate if (&p2get_mem_unum) 3340Sstevel@tonic-gate p2get_mem_unum = mc_get_mem_unum; 3350Sstevel@tonic-gate if (&p2get_mem_info) 3360Sstevel@tonic-gate p2get_mem_info = mc_get_mem_info; 3371186Sayznaga if (&p2get_mem_sid) 3381186Sayznaga p2get_mem_sid = mc_get_mem_sid; 3391186Sayznaga if (&p2get_mem_offset) 3401186Sayznaga p2get_mem_offset = mc_get_mem_offset; 3411186Sayznaga if (&p2get_mem_addr) 3421186Sayznaga p2get_mem_addr = mc_get_mem_addr; 3431186Sayznaga if (&p2init_sid_cache) 3441186Sayznaga p2init_sid_cache = mc_init_sid_cache; 3450Sstevel@tonic-gate } 3461186Sayznaga 3470Sstevel@tonic-gate mutex_exit(&mcmutex); 3480Sstevel@tonic-gate 3491186Sayznaga /* 3501186Sayznaga * Update DIMM serial id information if the DIMM serial id 3511186Sayznaga * cache has already been initialized. 3521186Sayznaga */ 3531186Sayznaga if (mc_dimm_sids) { 3541186Sayznaga rw_enter(&mcdimmsids_rw, RW_WRITER); 3551186Sayznaga (void) mc_populate_sid_cache(); 3561186Sayznaga rw_exit(&mcdimmsids_rw); 3571186Sayznaga } 3581186Sayznaga 3590Sstevel@tonic-gate if (ddi_create_minor_node(devi, "mc-us3", S_IFCHR, instance, 3600Sstevel@tonic-gate "ddi_mem_ctrl", 0) != DDI_SUCCESS) { 3610Sstevel@tonic-gate DPRINTF(MC_ATTACH_DEBUG, ("mc_attach: create_minor_node" 3620Sstevel@tonic-gate " failed \n")); 3630Sstevel@tonic-gate goto bad1; 3640Sstevel@tonic-gate } 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate ddi_report_dev(devi); 3670Sstevel@tonic-gate return (DDI_SUCCESS); 3680Sstevel@tonic-gate 3690Sstevel@tonic-gate bad1: 3700Sstevel@tonic-gate /* release all allocated data struture for this MC */ 3711186Sayznaga mlayout_del(softsp->portid, 0); 3720Sstevel@tonic-gate if (softsp->memlayoutp != NULL) 3730Sstevel@tonic-gate kmem_free(softsp->memlayoutp, softsp->size); 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate /* remove the libdevinfo property */ 3760Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip, 3770Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 3780Sstevel@tonic-gate MEM_CFG_PROP_NAME) == 1) { 3790Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip, 3800Sstevel@tonic-gate MEM_CFG_PROP_NAME); 3810Sstevel@tonic-gate } 3820Sstevel@tonic-gate 3830Sstevel@tonic-gate bad2: 3840Sstevel@tonic-gate /* unmap the registers for this device. */ 3850Sstevel@tonic-gate ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0); 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate bad: 3880Sstevel@tonic-gate ddi_soft_state_free(mcp, instance); 3890Sstevel@tonic-gate return (DDI_FAILURE); 3900Sstevel@tonic-gate } 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate /* ARGSUSED */ 3930Sstevel@tonic-gate static int 3940Sstevel@tonic-gate mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 3950Sstevel@tonic-gate { 3960Sstevel@tonic-gate int instance; 3970Sstevel@tonic-gate struct mc_soft_state *softsp; 3980Sstevel@tonic-gate 3990Sstevel@tonic-gate /* get the instance of this devi */ 4000Sstevel@tonic-gate instance = ddi_get_instance(devi); 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate /* get the soft state pointer for this device node */ 4030Sstevel@tonic-gate softsp = ddi_get_soft_state(mcp, instance); 4040Sstevel@tonic-gate 4050Sstevel@tonic-gate switch (cmd) { 4060Sstevel@tonic-gate case DDI_SUSPEND: 4070Sstevel@tonic-gate return (DDI_SUCCESS); 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate case DDI_DETACH: 4100Sstevel@tonic-gate break; 4110Sstevel@tonic-gate 4120Sstevel@tonic-gate default: 4130Sstevel@tonic-gate return (DDI_FAILURE); 4140Sstevel@tonic-gate } 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate DPRINTF(MC_DETACH_DEBUG, ("mc%d DETACH: portid= %d, table 0x%p\n", 4170Sstevel@tonic-gate instance, softsp->portid, softsp->memlayoutp)); 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate /* remove the libdevinfo property */ 4200Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip, 4210Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 4220Sstevel@tonic-gate MEM_CFG_PROP_NAME) == 1) { 4230Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, softsp->dip, 4240Sstevel@tonic-gate MEM_CFG_PROP_NAME); 4250Sstevel@tonic-gate } 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate /* release all allocated data struture for this MC */ 4281186Sayznaga mlayout_del(softsp->portid, 1); 4290Sstevel@tonic-gate if (softsp->memlayoutp != NULL) 4300Sstevel@tonic-gate kmem_free(softsp->memlayoutp, softsp->size); 4310Sstevel@tonic-gate 4320Sstevel@tonic-gate /* unmap the registers */ 4330Sstevel@tonic-gate ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->mc_base, 0, 0); 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate mutex_enter(&mcmutex); 4360Sstevel@tonic-gate if (nmcs == 0) { 4370Sstevel@tonic-gate if (&p2get_mem_unum) 4380Sstevel@tonic-gate p2get_mem_unum = NULL; 4390Sstevel@tonic-gate if (&p2get_mem_info) 4400Sstevel@tonic-gate p2get_mem_info = NULL; 4411186Sayznaga if (&p2get_mem_sid) 4421186Sayznaga p2get_mem_sid = NULL; 4431186Sayznaga if (&p2get_mem_offset) 4441186Sayznaga p2get_mem_offset = NULL; 4451186Sayznaga if (&p2get_mem_addr) 4461186Sayznaga p2get_mem_addr = NULL; 4471186Sayznaga if (&p2init_sid_cache) 4481186Sayznaga p2init_sid_cache = NULL; 4490Sstevel@tonic-gate } 4501186Sayznaga 4510Sstevel@tonic-gate mutex_exit(&mcmutex); 4520Sstevel@tonic-gate 4530Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate /* free up the soft state */ 4560Sstevel@tonic-gate ddi_soft_state_free(mcp, instance); 4570Sstevel@tonic-gate 4580Sstevel@tonic-gate return (DDI_SUCCESS); 4590Sstevel@tonic-gate } 4600Sstevel@tonic-gate 4610Sstevel@tonic-gate /* ARGSUSED */ 4620Sstevel@tonic-gate static int 4630Sstevel@tonic-gate mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 4640Sstevel@tonic-gate { 4650Sstevel@tonic-gate int status = 0; 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate /* verify that otyp is appropriate */ 4680Sstevel@tonic-gate if (otyp != OTYP_CHR) { 4690Sstevel@tonic-gate return (EINVAL); 4700Sstevel@tonic-gate } 4710Sstevel@tonic-gate 4720Sstevel@tonic-gate mutex_enter(&mcmutex); 4730Sstevel@tonic-gate if (mc_is_open) { 4740Sstevel@tonic-gate status = EBUSY; 4750Sstevel@tonic-gate goto bad; 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate mc_is_open = 1; 4780Sstevel@tonic-gate bad: 4790Sstevel@tonic-gate mutex_exit(&mcmutex); 4800Sstevel@tonic-gate return (status); 4810Sstevel@tonic-gate } 4820Sstevel@tonic-gate 4830Sstevel@tonic-gate /* ARGSUSED */ 4840Sstevel@tonic-gate static int 4850Sstevel@tonic-gate mc_close(dev_t devp, int flag, int otyp, cred_t *credp) 4860Sstevel@tonic-gate { 4870Sstevel@tonic-gate mutex_enter(&mcmutex); 4880Sstevel@tonic-gate mc_is_open = 0; 4890Sstevel@tonic-gate mutex_exit(&mcmutex); 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate return (0); 4920Sstevel@tonic-gate } 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate /* 4950Sstevel@tonic-gate * cmd includes MCIOC_MEMCONF, MCIOC_MEM, MCIOC_SEG, MCIOC_BANK, MCIOC_DEVGRP, 4960Sstevel@tonic-gate * MCIOC_CTRLCONF, MCIOC_CONTROL. 4970Sstevel@tonic-gate * 4980Sstevel@tonic-gate * MCIOC_MEM, MCIOC_SEG, MCIOC_CTRLCONF, and MCIOC_CONTROL are 4990Sstevel@tonic-gate * associated with various length struct. If given number is less than the 5000Sstevel@tonic-gate * number in kernel, update the number and return EINVAL so that user could 5010Sstevel@tonic-gate * allocate enough space for it. 5020Sstevel@tonic-gate * 5030Sstevel@tonic-gate */ 5040Sstevel@tonic-gate 5050Sstevel@tonic-gate /* ARGSUSED */ 5060Sstevel@tonic-gate static int 5070Sstevel@tonic-gate mc_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred_p, 5080Sstevel@tonic-gate int *rval_p) 5090Sstevel@tonic-gate { 5100Sstevel@tonic-gate size_t size; 5110Sstevel@tonic-gate struct mc_memconf mcmconf; 5120Sstevel@tonic-gate struct mc_memory *mcmem, mcmem_in; 5130Sstevel@tonic-gate struct mc_segment *mcseg, mcseg_in; 5140Sstevel@tonic-gate struct mc_bank mcbank; 5150Sstevel@tonic-gate struct mc_devgrp mcdevgrp; 5160Sstevel@tonic-gate struct mc_ctrlconf *mcctrlconf, mcctrlconf_in; 5170Sstevel@tonic-gate struct mc_control *mccontrol, mccontrol_in; 5180Sstevel@tonic-gate struct seg_info *seg = NULL; 5190Sstevel@tonic-gate struct bank_info *bank = NULL; 5200Sstevel@tonic-gate struct dgrp_info *dgrp = NULL; 5210Sstevel@tonic-gate struct mctrl_info *mcport; 5220Sstevel@tonic-gate mc_dlist_t *mctrl; 5230Sstevel@tonic-gate int i, status = 0; 5240Sstevel@tonic-gate cpu_t *cpu; 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate switch (cmd) { 5270Sstevel@tonic-gate case MCIOC_MEMCONF: 5280Sstevel@tonic-gate mutex_enter(&mcdatamutex); 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate mcmconf.nmcs = nmcs; 5310Sstevel@tonic-gate mcmconf.nsegments = nsegments; 5320Sstevel@tonic-gate mcmconf.nbanks = maxbanks; 5330Sstevel@tonic-gate mcmconf.ndevgrps = NDGRPS; 5340Sstevel@tonic-gate mcmconf.ndevs = NDIMMS; 5350Sstevel@tonic-gate mcmconf.len_dev = MAX_DEVLEN; 5360Sstevel@tonic-gate mcmconf.xfer_size = TRANSFER_SIZE; 5370Sstevel@tonic-gate 5380Sstevel@tonic-gate mutex_exit(&mcdatamutex); 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate if (copyout(&mcmconf, (void *)arg, sizeof (struct mc_memconf))) 5410Sstevel@tonic-gate return (EFAULT); 5420Sstevel@tonic-gate return (0); 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate /* 5450Sstevel@tonic-gate * input: nsegments and allocate space for various length of segmentids 5460Sstevel@tonic-gate * 5470Sstevel@tonic-gate * return 0: size, number of segments, and all segment ids, 5480Sstevel@tonic-gate * where glocal and local ids are identical. 5490Sstevel@tonic-gate * EINVAL: if the given nsegments is less than that in kernel and 5500Sstevel@tonic-gate * nsegments of struct will be updated. 5510Sstevel@tonic-gate * EFAULT: if other errors in kernel. 5520Sstevel@tonic-gate */ 5530Sstevel@tonic-gate case MCIOC_MEM: 5540Sstevel@tonic-gate if (copyin((void *)arg, &mcmem_in, 5550Sstevel@tonic-gate sizeof (struct mc_memory)) != 0) 5560Sstevel@tonic-gate return (EFAULT); 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate mutex_enter(&mcdatamutex); 5590Sstevel@tonic-gate if (mcmem_in.nsegments < nsegments) { 5600Sstevel@tonic-gate mcmem_in.nsegments = nsegments; 5610Sstevel@tonic-gate if (copyout(&mcmem_in, (void *)arg, 5620Sstevel@tonic-gate sizeof (struct mc_memory))) 5630Sstevel@tonic-gate status = EFAULT; 5640Sstevel@tonic-gate else 5650Sstevel@tonic-gate status = EINVAL; 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate mutex_exit(&mcdatamutex); 5680Sstevel@tonic-gate return (status); 5690Sstevel@tonic-gate } 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate size = sizeof (struct mc_memory) + (nsegments - 1) * 5720Sstevel@tonic-gate sizeof (mcmem->segmentids[0]); 5730Sstevel@tonic-gate mcmem = kmem_zalloc(size, KM_SLEEP); 5740Sstevel@tonic-gate 5750Sstevel@tonic-gate mcmem->size = memsize; 5760Sstevel@tonic-gate mcmem->nsegments = nsegments; 5770Sstevel@tonic-gate seg = (struct seg_info *)seg_head; 5780Sstevel@tonic-gate for (i = 0; i < nsegments; i++) { 5790Sstevel@tonic-gate ASSERT(seg != NULL); 5800Sstevel@tonic-gate mcmem->segmentids[i].globalid = seg->seg_node.id; 5810Sstevel@tonic-gate mcmem->segmentids[i].localid = seg->seg_node.id; 5820Sstevel@tonic-gate seg = (struct seg_info *)seg->seg_node.next; 5830Sstevel@tonic-gate } 5840Sstevel@tonic-gate mutex_exit(&mcdatamutex); 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate if (copyout(mcmem, (void *)arg, size)) 5870Sstevel@tonic-gate status = EFAULT; 5880Sstevel@tonic-gate 5890Sstevel@tonic-gate kmem_free(mcmem, size); 5900Sstevel@tonic-gate return (status); 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate /* 5930Sstevel@tonic-gate * input: id, nbanks and allocate space for various length of bankids 5940Sstevel@tonic-gate * 5950Sstevel@tonic-gate * return 0: base, size, number of banks, and all bank ids, 5960Sstevel@tonic-gate * where global id is unique of all banks and local id 5970Sstevel@tonic-gate * is only unique for mc. 5980Sstevel@tonic-gate * EINVAL: either id isn't found or if given nbanks is less than 5990Sstevel@tonic-gate * that in kernel and nbanks of struct will be updated. 6000Sstevel@tonic-gate * EFAULT: if other errors in kernel. 6010Sstevel@tonic-gate */ 6020Sstevel@tonic-gate case MCIOC_SEG: 6030Sstevel@tonic-gate 6040Sstevel@tonic-gate if (copyin((void *)arg, &mcseg_in, 6050Sstevel@tonic-gate sizeof (struct mc_segment)) != 0) 6060Sstevel@tonic-gate return (EFAULT); 6070Sstevel@tonic-gate 6080Sstevel@tonic-gate mutex_enter(&mcdatamutex); 6090Sstevel@tonic-gate if ((seg = (struct seg_info *)mc_node_get(mcseg_in.id, 6100Sstevel@tonic-gate seg_head)) == NULL) { 6110Sstevel@tonic-gate DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG: seg not match, " 6120Sstevel@tonic-gate "id %d\n", mcseg_in.id)); 6130Sstevel@tonic-gate mutex_exit(&mcdatamutex); 6140Sstevel@tonic-gate return (EFAULT); 6150Sstevel@tonic-gate } 6160Sstevel@tonic-gate 6170Sstevel@tonic-gate if (mcseg_in.nbanks < seg->nbanks) { 6180Sstevel@tonic-gate mcseg_in.nbanks = seg->nbanks; 6190Sstevel@tonic-gate if (copyout(&mcseg_in, (void *)arg, 6200Sstevel@tonic-gate sizeof (struct mc_segment))) 6210Sstevel@tonic-gate status = EFAULT; 6220Sstevel@tonic-gate else 6230Sstevel@tonic-gate status = EINVAL; 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate mutex_exit(&mcdatamutex); 6260Sstevel@tonic-gate return (status); 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate size = sizeof (struct mc_segment) + (seg->nbanks - 1) * 6300Sstevel@tonic-gate sizeof (mcseg->bankids[0]); 6310Sstevel@tonic-gate mcseg = kmem_zalloc(size, KM_SLEEP); 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate mcseg->id = seg->seg_node.id; 6340Sstevel@tonic-gate mcseg->ifactor = seg->ifactor; 6350Sstevel@tonic-gate mcseg->base = seg->base; 6360Sstevel@tonic-gate mcseg->size = seg->size; 6370Sstevel@tonic-gate mcseg->nbanks = seg->nbanks; 6380Sstevel@tonic-gate 6390Sstevel@tonic-gate bank = seg->hb_inseg; 6400Sstevel@tonic-gate 6410Sstevel@tonic-gate DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:nbanks %d seg 0x%p bank %p\n", 6420Sstevel@tonic-gate seg->nbanks, seg, bank)); 6430Sstevel@tonic-gate 6440Sstevel@tonic-gate i = 0; 6450Sstevel@tonic-gate while (bank != NULL) { 6460Sstevel@tonic-gate DPRINTF(MC_CMD_DEBUG, ("MCIOC_SEG:idx %d bank_id %d\n", 6470Sstevel@tonic-gate i, bank->bank_node.id)); 6480Sstevel@tonic-gate mcseg->bankids[i].globalid = bank->bank_node.id; 6490Sstevel@tonic-gate mcseg->bankids[i++].localid = 6500Sstevel@tonic-gate bank->local_id; 6510Sstevel@tonic-gate bank = bank->n_inseg; 6520Sstevel@tonic-gate } 6530Sstevel@tonic-gate ASSERT(i == seg->nbanks); 6540Sstevel@tonic-gate mutex_exit(&mcdatamutex); 6550Sstevel@tonic-gate 6560Sstevel@tonic-gate if (copyout(mcseg, (void *)arg, size)) 6570Sstevel@tonic-gate status = EFAULT; 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate kmem_free(mcseg, size); 6600Sstevel@tonic-gate return (status); 6610Sstevel@tonic-gate 6620Sstevel@tonic-gate /* 6630Sstevel@tonic-gate * input: id 6640Sstevel@tonic-gate * 6650Sstevel@tonic-gate * return 0: mask, match, size, and devgrpid, 6660Sstevel@tonic-gate * where global id is unique of all devgrps and local id 6670Sstevel@tonic-gate * is only unique for mc. 6680Sstevel@tonic-gate * EINVAL: if id isn't found 6690Sstevel@tonic-gate * EFAULT: if other errors in kernel. 6700Sstevel@tonic-gate */ 6710Sstevel@tonic-gate case MCIOC_BANK: 6720Sstevel@tonic-gate if (copyin((void *)arg, &mcbank, sizeof (struct mc_bank)) != 0) 6730Sstevel@tonic-gate return (EFAULT); 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank id %d\n", mcbank.id)); 6760Sstevel@tonic-gate 6770Sstevel@tonic-gate mutex_enter(&mcdatamutex); 6780Sstevel@tonic-gate 6790Sstevel@tonic-gate if ((bank = (struct bank_info *)mc_node_get(mcbank.id, 6800Sstevel@tonic-gate bank_head)) == NULL) { 6810Sstevel@tonic-gate mutex_exit(&mcdatamutex); 6820Sstevel@tonic-gate return (EINVAL); 6830Sstevel@tonic-gate } 6840Sstevel@tonic-gate 685946Smathue DPRINTF(MC_CMD_DEBUG, ("MCIOC_BANK: bank %d (0x%p) valid %hu\n", 6860Sstevel@tonic-gate bank->bank_node.id, bank, bank->valid)); 6870Sstevel@tonic-gate 6880Sstevel@tonic-gate /* 6890Sstevel@tonic-gate * If (Physic Address & MASK) == MATCH, Physic Address is 6900Sstevel@tonic-gate * located at this bank. The lower physical address bits 6910Sstevel@tonic-gate * are at [9-6]. 6920Sstevel@tonic-gate */ 6930Sstevel@tonic-gate mcbank.mask = (~(bank->lk | ~(MADR_LK_MASK >> 6940Sstevel@tonic-gate MADR_LK_SHIFT))) << MADR_LPA_SHIFT; 6950Sstevel@tonic-gate mcbank.match = bank->lm << MADR_LPA_SHIFT; 6960Sstevel@tonic-gate mcbank.size = bank->size; 6970Sstevel@tonic-gate mcbank.devgrpid.globalid = bank->devgrp_id; 6980Sstevel@tonic-gate mcbank.devgrpid.localid = bank->devgrp_id % NDGRPS; 6990Sstevel@tonic-gate 7000Sstevel@tonic-gate mutex_exit(&mcdatamutex); 7010Sstevel@tonic-gate 7020Sstevel@tonic-gate if (copyout(&mcbank, (void *)arg, sizeof (struct mc_bank))) 7030Sstevel@tonic-gate return (EFAULT); 7040Sstevel@tonic-gate return (0); 7050Sstevel@tonic-gate 7060Sstevel@tonic-gate /* 7070Sstevel@tonic-gate * input:id and allocate space for various length of deviceids 7080Sstevel@tonic-gate * 7090Sstevel@tonic-gate * return 0: size and number of devices. 7100Sstevel@tonic-gate * EINVAL: id isn't found 7110Sstevel@tonic-gate * EFAULT: if other errors in kernel. 7120Sstevel@tonic-gate */ 7130Sstevel@tonic-gate case MCIOC_DEVGRP: 7140Sstevel@tonic-gate 7150Sstevel@tonic-gate if (copyin((void *)arg, &mcdevgrp, 7160Sstevel@tonic-gate sizeof (struct mc_devgrp)) != 0) 7170Sstevel@tonic-gate return (EFAULT); 7180Sstevel@tonic-gate 7190Sstevel@tonic-gate mutex_enter(&mcdatamutex); 7200Sstevel@tonic-gate if ((dgrp = (struct dgrp_info *)mc_node_get(mcdevgrp.id, 7210Sstevel@tonic-gate dgrp_head)) == NULL) { 7220Sstevel@tonic-gate DPRINTF(MC_CMD_DEBUG, ("MCIOC_DEVGRP: not match, id " 7230Sstevel@tonic-gate "%d\n", mcdevgrp.id)); 7240Sstevel@tonic-gate mutex_exit(&mcdatamutex); 7250Sstevel@tonic-gate return (EINVAL); 7260Sstevel@tonic-gate } 7270Sstevel@tonic-gate 7280Sstevel@tonic-gate mcdevgrp.ndevices = dgrp->ndevices; 7290Sstevel@tonic-gate mcdevgrp.size = dgrp->size; 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate mutex_exit(&mcdatamutex); 7320Sstevel@tonic-gate 7330Sstevel@tonic-gate if (copyout(&mcdevgrp, (void *)arg, sizeof (struct mc_devgrp))) 7340Sstevel@tonic-gate status = EFAULT; 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate return (status); 7370Sstevel@tonic-gate 7380Sstevel@tonic-gate /* 7390Sstevel@tonic-gate * input: nmcs and allocate space for various length of mcids 7400Sstevel@tonic-gate * 7410Sstevel@tonic-gate * return 0: number of mc, and all mcids, 7420Sstevel@tonic-gate * where glocal and local ids are identical. 7430Sstevel@tonic-gate * EINVAL: if the given nmcs is less than that in kernel and 7440Sstevel@tonic-gate * nmcs of struct will be updated. 7450Sstevel@tonic-gate * EFAULT: if other errors in kernel. 7460Sstevel@tonic-gate */ 7470Sstevel@tonic-gate case MCIOC_CTRLCONF: 7480Sstevel@tonic-gate if (copyin((void *)arg, &mcctrlconf_in, 7490Sstevel@tonic-gate sizeof (struct mc_ctrlconf)) != 0) 7500Sstevel@tonic-gate return (EFAULT); 7510Sstevel@tonic-gate 7520Sstevel@tonic-gate mutex_enter(&mcdatamutex); 7530Sstevel@tonic-gate if (mcctrlconf_in.nmcs < nmcs) { 7540Sstevel@tonic-gate mcctrlconf_in.nmcs = nmcs; 7550Sstevel@tonic-gate if (copyout(&mcctrlconf_in, (void *)arg, 7560Sstevel@tonic-gate sizeof (struct mc_ctrlconf))) 7570Sstevel@tonic-gate status = EFAULT; 7580Sstevel@tonic-gate else 7590Sstevel@tonic-gate status = EINVAL; 7600Sstevel@tonic-gate 7610Sstevel@tonic-gate mutex_exit(&mcdatamutex); 7620Sstevel@tonic-gate return (status); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate 7650Sstevel@tonic-gate /* 7660Sstevel@tonic-gate * Cannot just use the size of the struct because of the various 7670Sstevel@tonic-gate * length struct 7680Sstevel@tonic-gate */ 7690Sstevel@tonic-gate size = sizeof (struct mc_ctrlconf) + ((nmcs - 1) * 7700Sstevel@tonic-gate sizeof (mcctrlconf->mcids[0])); 7710Sstevel@tonic-gate mcctrlconf = kmem_zalloc(size, KM_SLEEP); 7720Sstevel@tonic-gate 7730Sstevel@tonic-gate mcctrlconf->nmcs = nmcs; 7740Sstevel@tonic-gate 7750Sstevel@tonic-gate /* Get all MC ids and add to mcctrlconf */ 7760Sstevel@tonic-gate mctrl = mctrl_head; 7770Sstevel@tonic-gate i = 0; 7780Sstevel@tonic-gate while (mctrl != NULL) { 7790Sstevel@tonic-gate mcctrlconf->mcids[i].globalid = mctrl->id; 7800Sstevel@tonic-gate mcctrlconf->mcids[i].localid = mctrl->id; 7810Sstevel@tonic-gate i++; 7820Sstevel@tonic-gate mctrl = mctrl->next; 7830Sstevel@tonic-gate } 7840Sstevel@tonic-gate ASSERT(i == nmcs); 7850Sstevel@tonic-gate 7860Sstevel@tonic-gate mutex_exit(&mcdatamutex); 7870Sstevel@tonic-gate 7880Sstevel@tonic-gate if (copyout(mcctrlconf, (void *)arg, size)) 7890Sstevel@tonic-gate status = EFAULT; 7900Sstevel@tonic-gate 7910Sstevel@tonic-gate kmem_free(mcctrlconf, size); 7920Sstevel@tonic-gate return (status); 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate /* 7950Sstevel@tonic-gate * input:id, ndevgrps and allocate space for various length of devgrpids 7960Sstevel@tonic-gate * 7970Sstevel@tonic-gate * return 0: number of devgrp, and all devgrpids, 7980Sstevel@tonic-gate * is unique of all devgrps and local id is only unique 7990Sstevel@tonic-gate * for mc. 8000Sstevel@tonic-gate * EINVAL: either if id isn't found or if the given ndevgrps is 8010Sstevel@tonic-gate * less than that in kernel and ndevgrps of struct will 8020Sstevel@tonic-gate * be updated. 8030Sstevel@tonic-gate * EFAULT: if other errors in kernel. 8040Sstevel@tonic-gate */ 8050Sstevel@tonic-gate case MCIOC_CONTROL: 8060Sstevel@tonic-gate if (copyin((void *)arg, &mccontrol_in, 8070Sstevel@tonic-gate sizeof (struct mc_control)) != 0) 8080Sstevel@tonic-gate return (EFAULT); 8090Sstevel@tonic-gate 8100Sstevel@tonic-gate mutex_enter(&mcdatamutex); 8110Sstevel@tonic-gate if ((mcport = (struct mctrl_info *)mc_node_get(mccontrol_in.id, 8120Sstevel@tonic-gate mctrl_head)) == NULL) { 8130Sstevel@tonic-gate mutex_exit(&mcdatamutex); 8140Sstevel@tonic-gate return (EINVAL); 8150Sstevel@tonic-gate } 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate /* 8180Sstevel@tonic-gate * mcport->ndevgrps zero means Memory Controller is disable. 8190Sstevel@tonic-gate */ 8200Sstevel@tonic-gate if ((mccontrol_in.ndevgrps < mcport->ndevgrps) || 8210Sstevel@tonic-gate (mcport->ndevgrps == 0)) { 8220Sstevel@tonic-gate mccontrol_in.ndevgrps = mcport->ndevgrps; 8230Sstevel@tonic-gate if (copyout(&mccontrol_in, (void *)arg, 8240Sstevel@tonic-gate sizeof (struct mc_control))) 8250Sstevel@tonic-gate status = EFAULT; 8260Sstevel@tonic-gate else if (mcport->ndevgrps != 0) 8270Sstevel@tonic-gate status = EINVAL; 8280Sstevel@tonic-gate 8290Sstevel@tonic-gate mutex_exit(&mcdatamutex); 8300Sstevel@tonic-gate return (status); 8310Sstevel@tonic-gate } 8320Sstevel@tonic-gate 8330Sstevel@tonic-gate size = sizeof (struct mc_control) + (mcport->ndevgrps - 1) * 8340Sstevel@tonic-gate sizeof (mccontrol->devgrpids[0]); 8350Sstevel@tonic-gate mccontrol = kmem_zalloc(size, KM_SLEEP); 8360Sstevel@tonic-gate 8370Sstevel@tonic-gate mccontrol->id = mcport->mctrl_node.id; 8380Sstevel@tonic-gate mccontrol->ndevgrps = mcport->ndevgrps; 8390Sstevel@tonic-gate for (i = 0; i < mcport->ndevgrps; i++) { 8400Sstevel@tonic-gate mccontrol->devgrpids[i].globalid = mcport->devgrpids[i]; 8410Sstevel@tonic-gate mccontrol->devgrpids[i].localid = 8420Sstevel@tonic-gate mcport->devgrpids[i] % NDGRPS; 843946Smathue DPRINTF(MC_CMD_DEBUG, ("MCIOC_CONTROL: devgrp id %lu\n", 844946Smathue *(uint64_t *)&mccontrol->devgrpids[i])); 8450Sstevel@tonic-gate } 8460Sstevel@tonic-gate mutex_exit(&mcdatamutex); 8470Sstevel@tonic-gate 8480Sstevel@tonic-gate if (copyout(mccontrol, (void *)arg, size)) 8490Sstevel@tonic-gate status = EFAULT; 8500Sstevel@tonic-gate 8510Sstevel@tonic-gate kmem_free(mccontrol, size); 8520Sstevel@tonic-gate return (status); 8530Sstevel@tonic-gate 8540Sstevel@tonic-gate /* 8550Sstevel@tonic-gate * input:id 8560Sstevel@tonic-gate * 8570Sstevel@tonic-gate * return 0: CPU flushed successfully. 8580Sstevel@tonic-gate * EINVAL: the id wasn't found 8590Sstevel@tonic-gate */ 8600Sstevel@tonic-gate case MCIOC_ECFLUSH: 8610Sstevel@tonic-gate mutex_enter(&cpu_lock); 8620Sstevel@tonic-gate cpu = cpu_get((processorid_t)arg); 8630Sstevel@tonic-gate mutex_exit(&cpu_lock); 8640Sstevel@tonic-gate if (cpu == NULL) 8650Sstevel@tonic-gate return (EINVAL); 8660Sstevel@tonic-gate 8670Sstevel@tonic-gate xc_one(arg, (xcfunc_t *)cpu_flush_ecache, 0, 0); 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate return (0); 8700Sstevel@tonic-gate 8710Sstevel@tonic-gate default: 8720Sstevel@tonic-gate DPRINTF(MC_CMD_DEBUG, ("DEFAULT: cmd is wrong\n")); 8730Sstevel@tonic-gate return (EFAULT); 8740Sstevel@tonic-gate } 8750Sstevel@tonic-gate } 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate /* 8780Sstevel@tonic-gate * Get Memory Address Decoding Registers and construct list. 8790Sstevel@tonic-gate * flag is to workaround Cheetah's restriction where register cannot be mapped 8800Sstevel@tonic-gate * if port id(MC registers on it) == cpu id(process is running on it). 8810Sstevel@tonic-gate */ 8820Sstevel@tonic-gate static int 8830Sstevel@tonic-gate mc_get_mcregs(struct mc_soft_state *softsp) 8840Sstevel@tonic-gate { 8850Sstevel@tonic-gate int i; 8860Sstevel@tonic-gate int err = 0; 8870Sstevel@tonic-gate uint64_t madreg; 8880Sstevel@tonic-gate uint64_t ma_reg_array[NBANKS]; /* there are NBANKS of madrs */ 8890Sstevel@tonic-gate 8900Sstevel@tonic-gate /* Construct lists for MC, mctrl_info, dgrp_info, and device_info */ 8910Sstevel@tonic-gate mc_construct(softsp->portid, softsp->memlayoutp); 8920Sstevel@tonic-gate 8930Sstevel@tonic-gate /* 8940Sstevel@tonic-gate * If memlayoutp is NULL, the Memory Controller is disable, and 8950Sstevel@tonic-gate * doesn't need to create any bank and segment. 8960Sstevel@tonic-gate */ 8970Sstevel@tonic-gate if (softsp->memlayoutp == NULL) 8980Sstevel@tonic-gate goto exit; 8990Sstevel@tonic-gate 9000Sstevel@tonic-gate /* 9010Sstevel@tonic-gate * Get the content of 4 Memory Address Decoding Registers, and 9020Sstevel@tonic-gate * construct lists of logical banks and segments. 9030Sstevel@tonic-gate */ 9040Sstevel@tonic-gate for (i = 0; i < NBANKS; i++) { 9050Sstevel@tonic-gate DPRINTF(MC_REG_DEBUG, ("get_mcregs: mapreg=0x%p portid=%d " 9060Sstevel@tonic-gate "cpu=%d\n", softsp->mc_base, softsp->portid, CPU->cpu_id)); 9070Sstevel@tonic-gate 9080Sstevel@tonic-gate kpreempt_disable(); 9090Sstevel@tonic-gate if (softsp->portid == (cpunodes[CPU->cpu_id].portid)) 9100Sstevel@tonic-gate madreg = get_mcr(MADR0OFFSET + (i * REGOFFSET)); 9110Sstevel@tonic-gate else 9120Sstevel@tonic-gate madreg = *((uint64_t *)(softsp->mc_base + MADR0OFFSET + 9130Sstevel@tonic-gate (i * REGOFFSET))); 9140Sstevel@tonic-gate kpreempt_enable(); 9150Sstevel@tonic-gate 9160Sstevel@tonic-gate DPRINTF(MC_REG_DEBUG, ("get_mcregs 2: memlayoutp=0x%p madreg " 917946Smathue "reg=0x%lx\n", softsp->memlayoutp, madreg)); 9180Sstevel@tonic-gate 9190Sstevel@tonic-gate ma_reg_array[i] = madreg; 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate if ((err = mlayout_add(softsp->portid, i, madreg, 9220Sstevel@tonic-gate softsp->memlayoutp)) == -1) 9230Sstevel@tonic-gate break; 9240Sstevel@tonic-gate } 9250Sstevel@tonic-gate 9260Sstevel@tonic-gate /* 9270Sstevel@tonic-gate * Create the logical bank property for this mc node. This 9280Sstevel@tonic-gate * property is an encoded array of the madr for each logical 9290Sstevel@tonic-gate * bank (there are NBANKS of these). 9300Sstevel@tonic-gate */ 9310Sstevel@tonic-gate if (ddi_prop_exists(DDI_DEV_T_ANY, softsp->dip, 9320Sstevel@tonic-gate DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, 9330Sstevel@tonic-gate MEM_CFG_PROP_NAME) != 1) { 9340Sstevel@tonic-gate (void) ddi_prop_create(DDI_DEV_T_NONE, softsp->dip, 9350Sstevel@tonic-gate DDI_PROP_CANSLEEP, MEM_CFG_PROP_NAME, 9360Sstevel@tonic-gate (caddr_t)&ma_reg_array, sizeof (ma_reg_array)); 9370Sstevel@tonic-gate } 9380Sstevel@tonic-gate 9390Sstevel@tonic-gate exit: 9400Sstevel@tonic-gate if (!err) { 9410Sstevel@tonic-gate mutex_enter(&mcdatamutex); 9420Sstevel@tonic-gate nmcs++; 9430Sstevel@tonic-gate mutex_exit(&mcdatamutex); 9440Sstevel@tonic-gate } 9450Sstevel@tonic-gate return (err); 9460Sstevel@tonic-gate } 9470Sstevel@tonic-gate 9480Sstevel@tonic-gate /* 9491186Sayznaga * Translate a <DIMM, offset> pair to a physical address. 9501186Sayznaga */ 9511186Sayznaga static int 9521186Sayznaga mc_offset_to_addr(struct seg_info *seg, 9531186Sayznaga struct bank_info *bank, uint64_t off, uint64_t *addr) 9541186Sayznaga { 9551186Sayznaga uint64_t base, size, line, remainder; 9561186Sayznaga uint32_t ifactor; 9571186Sayznaga 9581186Sayznaga /* 9591186Sayznaga * Compute the half-dimm size in bytes. 9601186Sayznaga * Note that bank->size represents the number of data bytes, 9611186Sayznaga * and does not include the additional bits used for ecc, mtag, 9621186Sayznaga * and mtag ecc information in each 144-bit checkword. 9631186Sayznaga * For calculating the offset to a checkword we need the size 9641186Sayznaga * including the additional 8 bytes for each 64 data bytes of 9651186Sayznaga * a cache line. 9661186Sayznaga */ 9671186Sayznaga size = ((bank->size / 4) / 64) * 72; 9681186Sayznaga 9691186Sayznaga /* 9701186Sayznaga * Check if the offset is within this bank. This depends on the position 9711186Sayznaga * of the bank, i.e., whether it is the front bank or the back bank. 9721186Sayznaga */ 9731186Sayznaga base = size * bank->pos; 9741186Sayznaga 9751186Sayznaga if ((off < base) || (off >= (base + size))) 9761186Sayznaga return (-1); 9771186Sayznaga 9781186Sayznaga /* 9791186Sayznaga * Compute the offset within the half-dimm. 9801186Sayznaga */ 9811186Sayznaga off -= base; 9821186Sayznaga 9831186Sayznaga /* 9841186Sayznaga * Compute the line within the half-dimm. This is the same as the line 9851186Sayznaga * within the bank since each DIMM in a bank contributes uniformly 9861186Sayznaga * 144 bits (18 bytes) to a cache line. 9871186Sayznaga */ 9881186Sayznaga line = off / QWORD_SIZE_BYTES; 9891186Sayznaga 9901186Sayznaga remainder = off % QWORD_SIZE_BYTES; 9911186Sayznaga 9921186Sayznaga /* 9931186Sayznaga * Compute the line within the segment. 9941186Sayznaga * The bank->lm field indicates the order in which cache lines are 9951186Sayznaga * distributed across the banks of a segment (See the Cheetah PRM). 9961186Sayznaga * The interleave factor the bank is programmed with is used instead 9971186Sayznaga * of the segment interleave factor since a segment can be composed 9981186Sayznaga * of banks with different interleave factors if the banks are not 9991186Sayznaga * uniform in size. 10001186Sayznaga */ 10011186Sayznaga ifactor = (bank->lk ^ 0xF) + 1; 10021186Sayznaga line = (line * ifactor) + bank->lm; 10031186Sayznaga 10041186Sayznaga /* 10051186Sayznaga * Compute the physical address assuming that there are 64 data bytes 10061186Sayznaga * in a cache line. 10071186Sayznaga */ 10081186Sayznaga *addr = (line << 6) + seg->base; 10091186Sayznaga *addr += remainder * 16; 10101186Sayznaga 10111186Sayznaga return (0); 10121186Sayznaga } 10131186Sayznaga 10141186Sayznaga /* 10151186Sayznaga * Translate a physical address to a <DIMM, offset> pair. 10161186Sayznaga */ 10171186Sayznaga static void 10181186Sayznaga mc_addr_to_offset(struct seg_info *seg, 10191186Sayznaga struct bank_info *bank, uint64_t addr, uint64_t *off) 10201186Sayznaga { 10211186Sayznaga uint64_t base, size, line, remainder; 10221186Sayznaga uint32_t ifactor; 10231186Sayznaga 10241186Sayznaga /* 10251186Sayznaga * Compute the line within the segment assuming that there are 64 data 10261186Sayznaga * bytes in a cache line. 10271186Sayznaga */ 10281186Sayznaga line = (addr - seg->base) / 64; 10291186Sayznaga 10301186Sayznaga /* 10311186Sayznaga * The lm (lower match) field from the Memory Address Decoding Register 10321186Sayznaga * for this bank determines which lines within a memory segment this 10331186Sayznaga * bank should respond to. These are the actual address bits the 10341186Sayznaga * interleave is done over (See the Cheetah PRM). 10351186Sayznaga * In other words, the lm field indicates the order in which the cache 10361186Sayznaga * lines are distributed across the banks of a segment, and thusly it 10371186Sayznaga * can be used to compute the line within this bank. This is the same as 10381186Sayznaga * the line within the half-dimm. This is because each DIMM in a bank 10391186Sayznaga * contributes uniformly to every cache line. 10401186Sayznaga */ 10411186Sayznaga ifactor = (bank->lk ^ 0xF) + 1; 10421186Sayznaga line = (line - bank->lm)/ifactor; 10431186Sayznaga 10441186Sayznaga /* 10451186Sayznaga * Compute the offset within the half-dimm. This depends on whether 10461186Sayznaga * or not the bank is a front logical bank or a back logical bank. 10471186Sayznaga */ 10481186Sayznaga *off = line * QWORD_SIZE_BYTES; 10491186Sayznaga 10501186Sayznaga /* 10511186Sayznaga * Compute the half-dimm size in bytes. 10521186Sayznaga * Note that bank->size represents the number of data bytes, 10531186Sayznaga * and does not include the additional bits used for ecc, mtag, 10541186Sayznaga * and mtag ecc information in each 144-bit quadword. 10551186Sayznaga * For calculating the offset to a checkword we need the size 10561186Sayznaga * including the additional 8 bytes for each 64 data bytes of 10571186Sayznaga * a cache line. 10581186Sayznaga */ 10591186Sayznaga size = ((bank->size / 4) / 64) * 72; 10601186Sayznaga 10611186Sayznaga /* 10621186Sayznaga * Compute the offset within the dimm to the nearest line. This depends 10631186Sayznaga * on whether or not the bank is a front logical bank or a back logical 10641186Sayznaga * bank. 10651186Sayznaga */ 10661186Sayznaga base = size * bank->pos; 10671186Sayznaga *off += base; 10681186Sayznaga 10691186Sayznaga remainder = (addr - seg->base) % 64; 10701186Sayznaga remainder /= 16; 10711186Sayznaga *off += remainder; 10721186Sayznaga } 10731186Sayznaga 10741186Sayznaga /* 10750Sstevel@tonic-gate * A cache line is composed of four quadwords with the associated ECC, the 10760Sstevel@tonic-gate * MTag along with its associated ECC. This is depicted below: 10770Sstevel@tonic-gate * 10780Sstevel@tonic-gate * | Data | ECC | Mtag |MTag ECC| 10790Sstevel@tonic-gate * 127 0 8 0 2 0 3 0 10800Sstevel@tonic-gate * 10810Sstevel@tonic-gate * synd_code will be mapped as the following order to mc_get_mem_unum. 10820Sstevel@tonic-gate * 143 16 7 4 0 10830Sstevel@tonic-gate * 10840Sstevel@tonic-gate * | Quadword 0 | Quadword 1 | Quadword 2 | Quadword 3 | 10850Sstevel@tonic-gate * 575 432 431 288 287 144 143 0 10860Sstevel@tonic-gate * 10870Sstevel@tonic-gate * dimm table: each bit at a cache line needs two bits to present one of 10880Sstevel@tonic-gate * four dimms. So it needs 144 bytes(576 * 2 / 8). The content is in 10890Sstevel@tonic-gate * big edian order, i.e. dimm_table[0] presents for bit 572 to 575. 10900Sstevel@tonic-gate * 10910Sstevel@tonic-gate * pin table: each bit at a cache line needs one byte to present pin position, 10920Sstevel@tonic-gate * where max. is 230. So it needs 576 bytes. The order of table index is 10930Sstevel@tonic-gate * the same as bit position at a cache line, i.e. pin_table[0] presents 10940Sstevel@tonic-gate * for bit 0, Mtag ECC 0 of Quadword 3. 10950Sstevel@tonic-gate * 10960Sstevel@tonic-gate * This is a mapping from syndrome code to QuadWord Logical layout at Safari. 10970Sstevel@tonic-gate * Referring to Figure 3-4, Excalibur Architecture Manual. 10980Sstevel@tonic-gate * This table could be moved to cheetah.c if other platform teams agree with 10990Sstevel@tonic-gate * the bit layout at QuadWord. 11000Sstevel@tonic-gate */ 11010Sstevel@tonic-gate 11020Sstevel@tonic-gate static uint8_t qwordmap[] = 11030Sstevel@tonic-gate { 11040Sstevel@tonic-gate 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 11050Sstevel@tonic-gate 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 11060Sstevel@tonic-gate 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 11070Sstevel@tonic-gate 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 11080Sstevel@tonic-gate 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 11090Sstevel@tonic-gate 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 11100Sstevel@tonic-gate 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 11110Sstevel@tonic-gate 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 11120Sstevel@tonic-gate 7, 8, 9, 10, 11, 12, 13, 14, 15, 4, 5, 6, 0, 1, 2, 3, 11130Sstevel@tonic-gate }; 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate 11160Sstevel@tonic-gate /* ARGSUSED */ 11170Sstevel@tonic-gate static int 11180Sstevel@tonic-gate mc_get_mem_unum(int synd_code, uint64_t paddr, char *buf, int buflen, int *lenp) 11190Sstevel@tonic-gate { 11200Sstevel@tonic-gate int i, upper_pa, lower_pa, dimmoffset; 11210Sstevel@tonic-gate int quadword, pos_cacheline, position, index, idx4dimm; 11220Sstevel@tonic-gate int qwlayout = synd_code; 11230Sstevel@tonic-gate short offset, data; 11240Sstevel@tonic-gate char unum[UNUM_NAMLEN]; 11250Sstevel@tonic-gate struct dimm_info *dimmp; 11260Sstevel@tonic-gate struct pin_info *pinp; 11270Sstevel@tonic-gate struct bank_info *bank; 11280Sstevel@tonic-gate 11290Sstevel@tonic-gate /* 11300Sstevel@tonic-gate * Enforce old Openboot requirement for synd code, either a single-bit 11310Sstevel@tonic-gate * code from 0..QWORD_SIZE-1 or -1 (multi-bit error). 11320Sstevel@tonic-gate */ 11330Sstevel@tonic-gate if (qwlayout < -1 || qwlayout >= QWORD_SIZE) 11340Sstevel@tonic-gate return (EINVAL); 11350Sstevel@tonic-gate 11360Sstevel@tonic-gate unum[0] = '\0'; 11370Sstevel@tonic-gate 11380Sstevel@tonic-gate upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT; 11390Sstevel@tonic-gate lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT; 11400Sstevel@tonic-gate 11410Sstevel@tonic-gate DPRINTF(MC_GUNUM_DEBUG, ("qwlayout %d\n", qwlayout)); 11420Sstevel@tonic-gate 11430Sstevel@tonic-gate /* 11440Sstevel@tonic-gate * Scan all logical banks to get one responding to the physical 11450Sstevel@tonic-gate * address. Then compute the index to look up dimm and pin tables 11461186Sayznaga * to generate the unum. 11470Sstevel@tonic-gate */ 11480Sstevel@tonic-gate mutex_enter(&mcdatamutex); 11490Sstevel@tonic-gate bank = (struct bank_info *)bank_head; 11500Sstevel@tonic-gate while (bank != NULL) { 11510Sstevel@tonic-gate int bankid, mcid, bankno_permc; 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate bankid = bank->bank_node.id; 11540Sstevel@tonic-gate bankno_permc = bankid % NBANKS; 11550Sstevel@tonic-gate mcid = bankid / NBANKS; 11560Sstevel@tonic-gate 11570Sstevel@tonic-gate /* 11580Sstevel@tonic-gate * The Address Decoding logic decodes the different fields 11591186Sayznaga * in the Memory Address Decoding register to determine 11601186Sayznaga * whether a particular logical bank should respond to a 11610Sstevel@tonic-gate * physical address. 11620Sstevel@tonic-gate */ 11630Sstevel@tonic-gate if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) | 11640Sstevel@tonic-gate bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) { 11650Sstevel@tonic-gate bank = (struct bank_info *)bank->bank_node.next; 11660Sstevel@tonic-gate continue; 11670Sstevel@tonic-gate } 11680Sstevel@tonic-gate 11690Sstevel@tonic-gate dimmoffset = (bankno_permc % NDGRPS) * NDIMMS; 11700Sstevel@tonic-gate 11710Sstevel@tonic-gate dimmp = (struct dimm_info *)bank->dimminfop; 11720Sstevel@tonic-gate ASSERT(dimmp != NULL); 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate if ((qwlayout >= 0) && (qwlayout < QWORD_SIZE)) { 11750Sstevel@tonic-gate /* 11760Sstevel@tonic-gate * single-bit error handling, we can identify specific 11770Sstevel@tonic-gate * DIMM. 11780Sstevel@tonic-gate */ 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate pinp = (struct pin_info *)&dimmp->data[0]; 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate if (!dimmp->sym_flag) 11830Sstevel@tonic-gate pinp++; 11840Sstevel@tonic-gate 11850Sstevel@tonic-gate quadword = (paddr & 0x3f) / 16; 11860Sstevel@tonic-gate /* or quadword = (paddr >> 4) % 4; */ 11871186Sayznaga pos_cacheline = ((3 - quadword) * QWORD_SIZE) + 11880Sstevel@tonic-gate qwordmap[qwlayout]; 11890Sstevel@tonic-gate position = 575 - pos_cacheline; 11900Sstevel@tonic-gate index = position * 2 / 8; 11910Sstevel@tonic-gate offset = position % 4; 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate /* 11941186Sayznaga * Trade-off: We couldn't add pin number to 11951186Sayznaga * unum string because statistic number 11960Sstevel@tonic-gate * pumps up at the corresponding dimm not pin. 11970Sstevel@tonic-gate * (void) sprintf(unum, "Pin %1u ", (uint_t) 11980Sstevel@tonic-gate * pinp->pintable[pos_cacheline]); 11990Sstevel@tonic-gate */ 12000Sstevel@tonic-gate DPRINTF(MC_GUNUM_DEBUG, ("Pin number %1u\n", 12010Sstevel@tonic-gate (uint_t)pinp->pintable[pos_cacheline])); 12020Sstevel@tonic-gate data = pinp->dimmtable[index]; 12030Sstevel@tonic-gate idx4dimm = (data >> ((3 - offset) * 2)) & 3; 12040Sstevel@tonic-gate 12050Sstevel@tonic-gate (void) strncpy(unum, 12060Sstevel@tonic-gate (char *)dimmp->label[dimmoffset + idx4dimm], 12070Sstevel@tonic-gate UNUM_NAMLEN); 12080Sstevel@tonic-gate DPRINTF(MC_GUNUM_DEBUG, ("unum %s\n", unum)); 12090Sstevel@tonic-gate /* 12100Sstevel@tonic-gate * platform hook for adding label information to unum. 12110Sstevel@tonic-gate */ 12120Sstevel@tonic-gate mc_add_mem_unum_label(unum, mcid, bankno_permc, 12130Sstevel@tonic-gate idx4dimm); 12140Sstevel@tonic-gate } else { 12150Sstevel@tonic-gate char *p = unum; 12160Sstevel@tonic-gate size_t res = UNUM_NAMLEN; 12170Sstevel@tonic-gate 12180Sstevel@tonic-gate /* 12190Sstevel@tonic-gate * multi-bit error handling, we can only identify 12200Sstevel@tonic-gate * bank of DIMMs. 12210Sstevel@tonic-gate */ 12220Sstevel@tonic-gate 12230Sstevel@tonic-gate for (i = 0; (i < NDIMMS) && (res > 0); i++) { 12240Sstevel@tonic-gate (void) snprintf(p, res, "%s%s", 12250Sstevel@tonic-gate i == 0 ? "" : " ", 12260Sstevel@tonic-gate (char *)dimmp->label[dimmoffset + i]); 12270Sstevel@tonic-gate res -= strlen(p); 12280Sstevel@tonic-gate p += strlen(p); 12290Sstevel@tonic-gate } 12300Sstevel@tonic-gate 12310Sstevel@tonic-gate /* 12320Sstevel@tonic-gate * platform hook for adding label information 12330Sstevel@tonic-gate * to unum. 12340Sstevel@tonic-gate */ 12350Sstevel@tonic-gate mc_add_mem_unum_label(unum, mcid, bankno_permc, -1); 12360Sstevel@tonic-gate } 12370Sstevel@tonic-gate mutex_exit(&mcdatamutex); 12380Sstevel@tonic-gate if ((strlen(unum) >= UNUM_NAMLEN) || 12390Sstevel@tonic-gate (strlen(unum) >= buflen)) { 12400Sstevel@tonic-gate return (ENOSPC); 12410Sstevel@tonic-gate } else { 12420Sstevel@tonic-gate (void) strncpy(buf, unum, buflen); 12430Sstevel@tonic-gate *lenp = strlen(buf); 12440Sstevel@tonic-gate return (0); 12450Sstevel@tonic-gate } 12461186Sayznaga } /* end of while loop for logical bank list */ 12471186Sayznaga 12481186Sayznaga mutex_exit(&mcdatamutex); 12491186Sayznaga return (ENXIO); 12501186Sayznaga } 12511186Sayznaga 12521186Sayznaga /* ARGSUSED */ 12531186Sayznaga static int 12541186Sayznaga mc_get_mem_offset(uint64_t paddr, uint64_t *offp) 12551186Sayznaga { 12561186Sayznaga int upper_pa, lower_pa; 12571186Sayznaga struct bank_info *bank; 12581186Sayznaga struct seg_info *seg; 12591186Sayznaga 12601186Sayznaga upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT; 12611186Sayznaga lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT; 12621186Sayznaga 12631186Sayznaga /* 12641186Sayznaga * Scan all logical banks to get one responding to the physical 12651186Sayznaga * address. 12661186Sayznaga */ 12671186Sayznaga mutex_enter(&mcdatamutex); 12681186Sayznaga bank = (struct bank_info *)bank_head; 12691186Sayznaga while (bank != NULL) { 12701186Sayznaga /* 12711186Sayznaga * The Address Decoding logic decodes the different fields 12721186Sayznaga * in the Memory Address Decoding register to determine 12731186Sayznaga * whether a particular logical bank should respond to a 12741186Sayznaga * physical address. 12751186Sayznaga */ 12761186Sayznaga if ((!bank->valid) || ((~(~(upper_pa ^ bank->um) | 12771186Sayznaga bank->uk)) || (~(~(lower_pa ^ bank->lm) | bank->lk)))) { 12781186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 12791186Sayznaga continue; 12801186Sayznaga } 12811186Sayznaga 12821186Sayznaga seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head); 12831186Sayznaga ASSERT(seg != NULL); 12841186Sayznaga ASSERT(paddr >= seg->base); 12851186Sayznaga 12861186Sayznaga mc_addr_to_offset(seg, bank, paddr, offp); 12871186Sayznaga 12881186Sayznaga mutex_exit(&mcdatamutex); 12891186Sayznaga return (0); 12901186Sayznaga } 12910Sstevel@tonic-gate 12920Sstevel@tonic-gate mutex_exit(&mcdatamutex); 12930Sstevel@tonic-gate return (ENXIO); 12940Sstevel@tonic-gate } 12950Sstevel@tonic-gate 12961186Sayznaga /* 12971186Sayznaga * Translate a DIMM <id, offset> pair to a physical address. 12981186Sayznaga */ 12991186Sayznaga static int 13001186Sayznaga mc_get_mem_addr(int mcid, char *sid, uint64_t off, uint64_t *paddr) 13011186Sayznaga { 13021186Sayznaga struct seg_info *seg; 13031186Sayznaga struct bank_info *bank; 13041186Sayznaga int first_seg_id; 13051186Sayznaga int i, found; 13061186Sayznaga 13071186Sayznaga ASSERT(sid != NULL); 13081186Sayznaga 13091186Sayznaga mutex_enter(&mcdatamutex); 13101186Sayznaga 13111186Sayznaga rw_enter(&mcdimmsids_rw, RW_READER); 13121186Sayznaga 13131186Sayznaga /* 13141186Sayznaga * If DIMM serial ids have not been cached yet, tell the 13151186Sayznaga * caller to try again. 13161186Sayznaga */ 13171186Sayznaga if (mc_dimm_sids == NULL) { 13181186Sayznaga rw_exit(&mcdimmsids_rw); 13191186Sayznaga return (EAGAIN); 13201186Sayznaga } 13211186Sayznaga 13221186Sayznaga for (i = 0; i < max_entries; i++) { 13231186Sayznaga if (mc_dimm_sids[i].mcid == mcid) 13241186Sayznaga break; 13251186Sayznaga } 13261186Sayznaga 13271186Sayznaga if (i == max_entries) { 13281186Sayznaga rw_exit(&mcdimmsids_rw); 13291186Sayznaga mutex_exit(&mcdatamutex); 13301186Sayznaga return (ENODEV); 13311186Sayznaga } 13321186Sayznaga 13331186Sayznaga first_seg_id = mc_dimm_sids[i].seg_id; 13341186Sayznaga 13351186Sayznaga seg = (struct seg_info *)mc_node_get(first_seg_id, seg_head); 13361186Sayznaga 13371186Sayznaga rw_exit(&mcdimmsids_rw); 13381186Sayznaga 13391186Sayznaga if (seg == NULL) { 13401186Sayznaga mutex_exit(&mcdatamutex); 13411186Sayznaga return (ENODEV); 13421186Sayznaga } 13431186Sayznaga 13441186Sayznaga found = 0; 13451186Sayznaga 13461186Sayznaga for (bank = seg->hb_inseg; bank; bank = bank->n_inseg) { 13471186Sayznaga ASSERT(bank->valid); 13481186Sayznaga 13491186Sayznaga for (i = 0; i < NDIMMS; i++) { 13501186Sayznaga if (strncmp((char *)bank->dimmsidp[i], sid, 13511186Sayznaga DIMM_SERIAL_ID_LEN) == 0) 13521186Sayznaga break; 13531186Sayznaga } 13541186Sayznaga 13551186Sayznaga if (i == NDIMMS) 13561186Sayznaga continue; 13571186Sayznaga 13581186Sayznaga if (mc_offset_to_addr(seg, bank, off, paddr) == -1) 13591186Sayznaga continue; 13601186Sayznaga found = 1; 13611186Sayznaga break; 13621186Sayznaga } 13631186Sayznaga 13641186Sayznaga if (found) { 13651186Sayznaga mutex_exit(&mcdatamutex); 13661186Sayznaga return (0); 13671186Sayznaga } 13681186Sayznaga 13691186Sayznaga /* 13701186Sayznaga * If a bank wasn't found, it may be in another segment. 13711186Sayznaga * This can happen if the different logical banks of an MC 13721186Sayznaga * have different interleave factors. To deal with this 13731186Sayznaga * possibility, we'll do a brute-force search for banks 13741186Sayznaga * for this MC with a different seg id then above. 13751186Sayznaga */ 13761186Sayznaga bank = (struct bank_info *)bank_head; 13771186Sayznaga while (bank != NULL) { 13781186Sayznaga 13791186Sayznaga if (!bank->valid) { 13801186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 13811186Sayznaga continue; 13821186Sayznaga } 13831186Sayznaga 13841186Sayznaga if (bank->bank_node.id / NBANKS != mcid) { 13851186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 13861186Sayznaga continue; 13871186Sayznaga } 13881186Sayznaga 13891186Sayznaga /* Ignore banks in the segment we looked in above. */ 13901186Sayznaga if (bank->seg_id == mc_dimm_sids[i].seg_id) { 13911186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 13921186Sayznaga continue; 13931186Sayznaga } 13941186Sayznaga 13951186Sayznaga for (i = 0; i < NDIMMS; i++) { 13961186Sayznaga if (strncmp((char *)bank->dimmsidp[i], sid, 13971186Sayznaga DIMM_SERIAL_ID_LEN) == 0) 13981186Sayznaga break; 13991186Sayznaga } 14001186Sayznaga 14011186Sayznaga if (i == NDIMMS) { 14021186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 14031186Sayznaga continue; 14041186Sayznaga } 14051186Sayznaga 14061186Sayznaga seg = (struct seg_info *)mc_node_get(bank->seg_id, seg_head); 14071186Sayznaga 14081186Sayznaga if (mc_offset_to_addr(seg, bank, off, paddr) == -1) { 14091186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 14101186Sayznaga continue; 14111186Sayznaga } 14121186Sayznaga 14131186Sayznaga found = 1; 14141186Sayznaga break; 14151186Sayznaga } 14161186Sayznaga 14171186Sayznaga mutex_exit(&mcdatamutex); 14181186Sayznaga 14191186Sayznaga if (found) 14201186Sayznaga return (0); 14211186Sayznaga else 14221186Sayznaga return (ENOENT); 14231186Sayznaga } 14241186Sayznaga 14250Sstevel@tonic-gate static int 14260Sstevel@tonic-gate mc_get_mem_info(int synd_code, uint64_t paddr, 14270Sstevel@tonic-gate uint64_t *mem_sizep, uint64_t *seg_sizep, uint64_t *bank_sizep, 14280Sstevel@tonic-gate int *segsp, int *banksp, int *mcidp) 14290Sstevel@tonic-gate { 14300Sstevel@tonic-gate int upper_pa, lower_pa; 14310Sstevel@tonic-gate struct bank_info *bankp; 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate if (synd_code < -1 || synd_code >= QWORD_SIZE) 14340Sstevel@tonic-gate return (EINVAL); 14350Sstevel@tonic-gate 14360Sstevel@tonic-gate upper_pa = (paddr & MADR_UPA_MASK) >> MADR_UPA_SHIFT; 14370Sstevel@tonic-gate lower_pa = (paddr & MADR_LPA_MASK) >> MADR_LPA_SHIFT; 14380Sstevel@tonic-gate 14390Sstevel@tonic-gate /* 14400Sstevel@tonic-gate * Scan all logical banks to get one responding to the physical 14410Sstevel@tonic-gate * address. 14420Sstevel@tonic-gate */ 14430Sstevel@tonic-gate mutex_enter(&mcdatamutex); 14440Sstevel@tonic-gate bankp = (struct bank_info *)bank_head; 14450Sstevel@tonic-gate while (bankp != NULL) { 14460Sstevel@tonic-gate struct seg_info *segp; 14470Sstevel@tonic-gate int bankid, mcid; 14480Sstevel@tonic-gate 14490Sstevel@tonic-gate bankid = bankp->bank_node.id; 14500Sstevel@tonic-gate mcid = bankid / NBANKS; 14510Sstevel@tonic-gate 14520Sstevel@tonic-gate /* 14530Sstevel@tonic-gate * The Address Decoding logic decodes the different fields 14540Sstevel@tonic-gate * in the Memory Address Decoding register to determine 14551186Sayznaga * whether a particular logical bank should respond to a 14560Sstevel@tonic-gate * physical address. 14570Sstevel@tonic-gate */ 14580Sstevel@tonic-gate if ((!bankp->valid) || ((~(~(upper_pa ^ bankp->um) | 14590Sstevel@tonic-gate bankp->uk)) || (~(~(lower_pa ^ bankp->lm) | bankp->lk)))) { 14600Sstevel@tonic-gate bankp = (struct bank_info *)bankp->bank_node.next; 14610Sstevel@tonic-gate continue; 14620Sstevel@tonic-gate } 14630Sstevel@tonic-gate 14640Sstevel@tonic-gate /* 14650Sstevel@tonic-gate * Get the corresponding segment. 14660Sstevel@tonic-gate */ 14670Sstevel@tonic-gate if ((segp = (struct seg_info *)mc_node_get(bankp->seg_id, 14680Sstevel@tonic-gate seg_head)) == NULL) { 14690Sstevel@tonic-gate mutex_exit(&mcdatamutex); 14700Sstevel@tonic-gate return (EFAULT); 14710Sstevel@tonic-gate } 14720Sstevel@tonic-gate 14730Sstevel@tonic-gate *mem_sizep = memsize; 14740Sstevel@tonic-gate *seg_sizep = segp->size; 14750Sstevel@tonic-gate *bank_sizep = bankp->size; 14760Sstevel@tonic-gate *segsp = nsegments; 14770Sstevel@tonic-gate *banksp = segp->nbanks; 14780Sstevel@tonic-gate *mcidp = mcid; 14790Sstevel@tonic-gate 14800Sstevel@tonic-gate mutex_exit(&mcdatamutex); 14810Sstevel@tonic-gate 14820Sstevel@tonic-gate return (0); 14830Sstevel@tonic-gate 14841186Sayznaga } /* end of while loop for logical bank list */ 14850Sstevel@tonic-gate 14860Sstevel@tonic-gate mutex_exit(&mcdatamutex); 14870Sstevel@tonic-gate return (ENXIO); 14880Sstevel@tonic-gate } 14890Sstevel@tonic-gate 14900Sstevel@tonic-gate /* 14910Sstevel@tonic-gate * Construct lists for an enabled MC where size of memory is 0. 14920Sstevel@tonic-gate * The lists are connected as follows: 14930Sstevel@tonic-gate * Attached MC -> device group list -> device list(per devgrp). 14940Sstevel@tonic-gate */ 14950Sstevel@tonic-gate static void 14960Sstevel@tonic-gate mc_construct(int mc_id, void *dimminfop) 14970Sstevel@tonic-gate { 14980Sstevel@tonic-gate int i, j, idx, dmidx; 14990Sstevel@tonic-gate struct mctrl_info *mctrl; 15000Sstevel@tonic-gate struct dgrp_info *dgrp; 15010Sstevel@tonic-gate struct device_info *dev; 15020Sstevel@tonic-gate struct dimm_info *dimmp = (struct dimm_info *)dimminfop; 15030Sstevel@tonic-gate 15040Sstevel@tonic-gate mutex_enter(&mcdatamutex); 15050Sstevel@tonic-gate /* allocate for mctrl_info and bank_info */ 15060Sstevel@tonic-gate if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id, 15070Sstevel@tonic-gate mctrl_head)) != NULL) { 15080Sstevel@tonic-gate cmn_err(CE_WARN, "mc_construct: mctrl %d exists\n", mc_id); 15090Sstevel@tonic-gate mutex_exit(&mcdatamutex); 15100Sstevel@tonic-gate return; 15110Sstevel@tonic-gate } 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate mctrl = kmem_zalloc(sizeof (struct mctrl_info), KM_SLEEP); 15140Sstevel@tonic-gate 15150Sstevel@tonic-gate /* 15160Sstevel@tonic-gate * If dimminfop is NULL, the Memory Controller is disable, and 15170Sstevel@tonic-gate * the number of device group will be zero. 15180Sstevel@tonic-gate */ 15190Sstevel@tonic-gate if (dimminfop == NULL) { 15200Sstevel@tonic-gate mctrl->mctrl_node.id = mc_id; 15210Sstevel@tonic-gate mctrl->ndevgrps = 0; 15220Sstevel@tonic-gate mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail); 15230Sstevel@tonic-gate mutex_exit(&mcdatamutex); 15240Sstevel@tonic-gate return; 15250Sstevel@tonic-gate } 15260Sstevel@tonic-gate 15270Sstevel@tonic-gate /* add the entry on dgrp_info list */ 15280Sstevel@tonic-gate for (i = 0; i < NDGRPS; i++) { 15290Sstevel@tonic-gate idx = mc_id * NDGRPS + i; 15300Sstevel@tonic-gate mctrl->devgrpids[i] = idx; 15310Sstevel@tonic-gate if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head)) 15320Sstevel@tonic-gate != NULL) { 15330Sstevel@tonic-gate cmn_err(CE_WARN, "mc_construct: devgrp %d exists\n", 15340Sstevel@tonic-gate idx); 15350Sstevel@tonic-gate continue; 15360Sstevel@tonic-gate } 15370Sstevel@tonic-gate 15380Sstevel@tonic-gate dgrp = kmem_zalloc(sizeof (struct dgrp_info), KM_SLEEP); 15390Sstevel@tonic-gate 15400Sstevel@tonic-gate /* add the entry on device_info list */ 15410Sstevel@tonic-gate for (j = 0; j < NDIMMS; j++) { 15420Sstevel@tonic-gate dmidx = idx * NDIMMS + j; 15430Sstevel@tonic-gate dgrp->deviceids[j] = dmidx; 15440Sstevel@tonic-gate if ((dev = (struct device_info *) 15450Sstevel@tonic-gate mc_node_get(dmidx, device_head)) != NULL) { 15460Sstevel@tonic-gate cmn_err(CE_WARN, "mc_construct: device %d " 15470Sstevel@tonic-gate "exists\n", dmidx); 15480Sstevel@tonic-gate continue; 15490Sstevel@tonic-gate } 15500Sstevel@tonic-gate dev = kmem_zalloc(sizeof (struct device_info), 15510Sstevel@tonic-gate KM_SLEEP); 15520Sstevel@tonic-gate dev->dev_node.id = dmidx; 15530Sstevel@tonic-gate dev->size = 0; 15540Sstevel@tonic-gate (void) strncpy(dev->label, (char *) 15550Sstevel@tonic-gate dimmp->label[i * NDIMMS + j], MAX_DEVLEN); 15560Sstevel@tonic-gate 15570Sstevel@tonic-gate mc_node_add((mc_dlist_t *)dev, &device_head, 15580Sstevel@tonic-gate &device_tail); 15590Sstevel@tonic-gate } /* for loop for constructing device_info */ 15600Sstevel@tonic-gate 15610Sstevel@tonic-gate dgrp->dgrp_node.id = idx; 15620Sstevel@tonic-gate dgrp->ndevices = NDIMMS; 15630Sstevel@tonic-gate dgrp->size = 0; 15640Sstevel@tonic-gate mc_node_add((mc_dlist_t *)dgrp, &dgrp_head, &dgrp_tail); 15650Sstevel@tonic-gate 15660Sstevel@tonic-gate } /* end of for loop for constructing dgrp_info list */ 15670Sstevel@tonic-gate 15680Sstevel@tonic-gate mctrl->mctrl_node.id = mc_id; 15690Sstevel@tonic-gate mctrl->ndevgrps = NDGRPS; 15700Sstevel@tonic-gate mc_node_add((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail); 15710Sstevel@tonic-gate mutex_exit(&mcdatamutex); 15720Sstevel@tonic-gate } 15730Sstevel@tonic-gate 15740Sstevel@tonic-gate /* 15750Sstevel@tonic-gate * Construct lists for Memory Configuration at logical viewpoint. 15760Sstevel@tonic-gate * 15770Sstevel@tonic-gate * Retrieve information from Memory Address Decoding Register and set up 15780Sstevel@tonic-gate * bank and segment lists. Link bank to its corresponding device group, and 15790Sstevel@tonic-gate * update size of device group and devices. Also connect bank to the segment. 15800Sstevel@tonic-gate * 15810Sstevel@tonic-gate * Memory Address Decoding Register 15820Sstevel@tonic-gate * ------------------------------------------------------------------------- 15830Sstevel@tonic-gate * |63|62 53|52 41|40 37|36 20|19 18|17 14|13 12|11 8|7 0| 15840Sstevel@tonic-gate * |-----------|----------|------|---------|-----|------|-----|-----|-------| 15850Sstevel@tonic-gate * |V | - | UK | - | UM | - | LK | - | LM | - | 15860Sstevel@tonic-gate * ------------------------------------------------------------------------- 15870Sstevel@tonic-gate * 15880Sstevel@tonic-gate */ 15890Sstevel@tonic-gate 15900Sstevel@tonic-gate static int 15910Sstevel@tonic-gate mlayout_add(int mc_id, int bank_no, uint64_t reg, void *dimminfop) 15920Sstevel@tonic-gate { 15930Sstevel@tonic-gate int i, dmidx, idx; 15940Sstevel@tonic-gate uint32_t ifactor; 15950Sstevel@tonic-gate int status = 0; 15960Sstevel@tonic-gate uint64_t size, base; 15970Sstevel@tonic-gate struct seg_info *seg_curr; 15980Sstevel@tonic-gate struct bank_info *bank_curr; 15990Sstevel@tonic-gate struct dgrp_info *dgrp; 16000Sstevel@tonic-gate struct device_info *dev; 16010Sstevel@tonic-gate union { 16020Sstevel@tonic-gate struct { 16030Sstevel@tonic-gate uint64_t valid : 1; 16040Sstevel@tonic-gate uint64_t resrv1 : 10; 16050Sstevel@tonic-gate uint64_t uk : 12; 16060Sstevel@tonic-gate uint64_t resrv2 : 4; 16070Sstevel@tonic-gate uint64_t um : 17; 16080Sstevel@tonic-gate uint64_t resrv3 : 2; 16090Sstevel@tonic-gate uint64_t lk : 4; 16100Sstevel@tonic-gate uint64_t resrv4 : 2; 16110Sstevel@tonic-gate uint64_t lm : 4; 16120Sstevel@tonic-gate uint64_t resrv5 : 8; 16130Sstevel@tonic-gate } _s; 16140Sstevel@tonic-gate uint64_t madreg; 16150Sstevel@tonic-gate } mcreg; 16160Sstevel@tonic-gate 16170Sstevel@tonic-gate mcreg.madreg = reg; 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: mc_id %d, bank num " 1620946Smathue "%d, reg 0x%lx\n", mc_id, bank_no, reg)); 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate /* add the entry on bank_info list */ 16230Sstevel@tonic-gate idx = mc_id * NBANKS + bank_no; 16240Sstevel@tonic-gate 16250Sstevel@tonic-gate mutex_enter(&mcdatamutex); 16260Sstevel@tonic-gate if ((bank_curr = (struct bank_info *)mc_node_get(idx, bank_head)) 16270Sstevel@tonic-gate != NULL) { 16280Sstevel@tonic-gate cmn_err(CE_WARN, "mlayout_add: bank %d exists\n", bank_no); 16290Sstevel@tonic-gate goto exit; 16300Sstevel@tonic-gate } 16310Sstevel@tonic-gate 16320Sstevel@tonic-gate bank_curr = kmem_zalloc(sizeof (struct bank_info), KM_SLEEP); 16330Sstevel@tonic-gate bank_curr->bank_node.id = idx; 16340Sstevel@tonic-gate bank_curr->valid = mcreg._s.valid; 16350Sstevel@tonic-gate bank_curr->dimminfop = dimminfop; 16360Sstevel@tonic-gate 16370Sstevel@tonic-gate if (!mcreg._s.valid) { 16380Sstevel@tonic-gate mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail); 16390Sstevel@tonic-gate goto exit; 16400Sstevel@tonic-gate } 16410Sstevel@tonic-gate 16420Sstevel@tonic-gate /* 16430Sstevel@tonic-gate * size of a logical bank = size of segment / interleave factor 16440Sstevel@tonic-gate * This fomula is not only working for regular configuration, 16450Sstevel@tonic-gate * i.e. number of banks at a segment equals to the max 16460Sstevel@tonic-gate * interleave factor, but also for special case, say 3 bank 16470Sstevel@tonic-gate * interleave. One bank is 2 way interleave and other two are 16480Sstevel@tonic-gate * 4 way. So the sizes of banks are size of segment/2 and /4 16490Sstevel@tonic-gate * respectively. 16500Sstevel@tonic-gate */ 16510Sstevel@tonic-gate ifactor = (mcreg._s.lk ^ 0xF) + 1; 16520Sstevel@tonic-gate size = (((mcreg._s.uk & 0x3FF) + 1) * 0x4000000) / ifactor; 16530Sstevel@tonic-gate base = mcreg._s.um & ~mcreg._s.uk; 16540Sstevel@tonic-gate base <<= MADR_UPA_SHIFT; 16550Sstevel@tonic-gate 16560Sstevel@tonic-gate bank_curr->uk = mcreg._s.uk; 16570Sstevel@tonic-gate bank_curr->um = mcreg._s.um; 16580Sstevel@tonic-gate bank_curr->lk = mcreg._s.lk; 16590Sstevel@tonic-gate bank_curr->lm = mcreg._s.lm; 16600Sstevel@tonic-gate bank_curr->size = size; 16610Sstevel@tonic-gate 16621186Sayznaga /* 16631186Sayznaga * The bank's position depends on which halves of the DIMMs it consists 16641186Sayznaga * of. The front-side halves of the 4 DIMMs constitute the front bank 16651186Sayznaga * and the back-side halves constitute the back bank. Bank numbers 16661186Sayznaga * 0 and 1 are front-side banks and bank numbers 2 and 3 are back side 16671186Sayznaga * banks. 16681186Sayznaga */ 16691186Sayznaga bank_curr->pos = bank_no >> 1; 16701186Sayznaga ASSERT((bank_curr->pos == 0) || (bank_curr->pos == 1)); 16711186Sayznaga 16720Sstevel@tonic-gate DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add 3: logical bank num %d, " 1673946Smathue "lk 0x%x uk 0x%x um 0x%x ifactor 0x%x size 0x%lx base 0x%lx\n", 1674946Smathue idx, mcreg._s.lk, mcreg._s.uk, mcreg._s.um, ifactor, size, base)); 16750Sstevel@tonic-gate 16760Sstevel@tonic-gate /* connect the entry and update the size on dgrp_info list */ 16770Sstevel@tonic-gate idx = mc_id * NDGRPS + (bank_no % NDGRPS); 16780Sstevel@tonic-gate if ((dgrp = (struct dgrp_info *)mc_node_get(idx, dgrp_head)) == NULL) { 16790Sstevel@tonic-gate /* all avaiable dgrp should be linked at mc_construct */ 16800Sstevel@tonic-gate cmn_err(CE_WARN, "mlayout_add: dgrp %d doesn't exist\n", idx); 16810Sstevel@tonic-gate kmem_free(bank_curr, sizeof (struct bank_info)); 16820Sstevel@tonic-gate status = -1; 16830Sstevel@tonic-gate goto exit; 16840Sstevel@tonic-gate } 16850Sstevel@tonic-gate 16860Sstevel@tonic-gate bank_curr->devgrp_id = idx; 16870Sstevel@tonic-gate dgrp->size += size; 16880Sstevel@tonic-gate 16890Sstevel@tonic-gate /* Update the size of entry on device_info list */ 16900Sstevel@tonic-gate for (i = 0; i < NDIMMS; i++) { 16910Sstevel@tonic-gate dmidx = dgrp->dgrp_node.id * NDIMMS + i; 16920Sstevel@tonic-gate dgrp->deviceids[i] = dmidx; 16930Sstevel@tonic-gate 16940Sstevel@tonic-gate /* avaiable device should be linked at mc_construct */ 16950Sstevel@tonic-gate if ((dev = (struct device_info *)mc_node_get(dmidx, 16960Sstevel@tonic-gate device_head)) == NULL) { 16970Sstevel@tonic-gate cmn_err(CE_WARN, "mlayout_add:dev %d doesn't exist\n", 16980Sstevel@tonic-gate dmidx); 16990Sstevel@tonic-gate kmem_free(bank_curr, sizeof (struct bank_info)); 17000Sstevel@tonic-gate status = -1; 17010Sstevel@tonic-gate goto exit; 17020Sstevel@tonic-gate } 17030Sstevel@tonic-gate 17040Sstevel@tonic-gate dev->size += (size / NDIMMS); 17050Sstevel@tonic-gate 1706946Smathue DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add DIMM:id %d, size %lu\n", 17070Sstevel@tonic-gate dmidx, size)); 17080Sstevel@tonic-gate } 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate /* 17110Sstevel@tonic-gate * Get the segment by matching the base address, link this bank 17120Sstevel@tonic-gate * to the segment. If not matched, allocate a new segment and 17130Sstevel@tonic-gate * add it at segment list. 17140Sstevel@tonic-gate */ 17150Sstevel@tonic-gate if (seg_curr = seg_match_base(base)) { 17160Sstevel@tonic-gate seg_curr->nbanks++; 17170Sstevel@tonic-gate seg_curr->size += size; 17180Sstevel@tonic-gate if (ifactor > seg_curr->ifactor) 17190Sstevel@tonic-gate seg_curr->ifactor = ifactor; 17200Sstevel@tonic-gate bank_curr->seg_id = seg_curr->seg_node.id; 17210Sstevel@tonic-gate } else { 17220Sstevel@tonic-gate seg_curr = (struct seg_info *) 17230Sstevel@tonic-gate kmem_zalloc(sizeof (struct seg_info), KM_SLEEP); 17240Sstevel@tonic-gate bank_curr->seg_id = seg_id; 17250Sstevel@tonic-gate seg_curr->seg_node.id = seg_id++; 17260Sstevel@tonic-gate seg_curr->base = base; 17270Sstevel@tonic-gate seg_curr->size = size; 17280Sstevel@tonic-gate seg_curr->nbanks = 1; 17290Sstevel@tonic-gate seg_curr->ifactor = ifactor; 17300Sstevel@tonic-gate mc_node_add((mc_dlist_t *)seg_curr, &seg_head, &seg_tail); 17310Sstevel@tonic-gate 17320Sstevel@tonic-gate nsegments++; 17330Sstevel@tonic-gate } 17340Sstevel@tonic-gate 17350Sstevel@tonic-gate /* Get the local id of bank which is only unique per segment. */ 17360Sstevel@tonic-gate bank_curr->local_id = seg_curr->nbanks - 1; 17370Sstevel@tonic-gate 17380Sstevel@tonic-gate /* add bank at the end of the list; not sorted by bankid */ 17390Sstevel@tonic-gate if (seg_curr->hb_inseg != NULL) { 17400Sstevel@tonic-gate bank_curr->p_inseg = seg_curr->tb_inseg; 17410Sstevel@tonic-gate bank_curr->n_inseg = seg_curr->tb_inseg->n_inseg; 17420Sstevel@tonic-gate seg_curr->tb_inseg->n_inseg = bank_curr; 17430Sstevel@tonic-gate seg_curr->tb_inseg = bank_curr; 17440Sstevel@tonic-gate } else { 17450Sstevel@tonic-gate bank_curr->n_inseg = bank_curr->p_inseg = NULL; 17460Sstevel@tonic-gate seg_curr->hb_inseg = seg_curr->tb_inseg = bank_curr; 17470Sstevel@tonic-gate } 17480Sstevel@tonic-gate DPRINTF(MC_CNSTRC_DEBUG, ("mlayout_add: + bank to seg, id %d\n", 17490Sstevel@tonic-gate seg_curr->seg_node.id)); 17500Sstevel@tonic-gate 1751*1463Sayznaga if (mc_dimm_sids) { 1752*1463Sayznaga rw_enter(&mcdimmsids_rw, RW_WRITER); 1753*1463Sayznaga mc_update_bank(bank_curr); 1754*1463Sayznaga rw_exit(&mcdimmsids_rw); 1755*1463Sayznaga } 17560Sstevel@tonic-gate mc_node_add((mc_dlist_t *)bank_curr, &bank_head, &bank_tail); 17570Sstevel@tonic-gate 17580Sstevel@tonic-gate memsize += size; 17590Sstevel@tonic-gate if (seg_curr->nbanks > maxbanks) 17600Sstevel@tonic-gate maxbanks = seg_curr->nbanks; 17610Sstevel@tonic-gate 17620Sstevel@tonic-gate exit: 17630Sstevel@tonic-gate mutex_exit(&mcdatamutex); 17640Sstevel@tonic-gate return (status); 17650Sstevel@tonic-gate } 17660Sstevel@tonic-gate 17670Sstevel@tonic-gate /* 17680Sstevel@tonic-gate * Delete nodes related to the given MC on mc, device group, device, 17690Sstevel@tonic-gate * and bank lists. Moreover, delete corresponding segment if its connected 17700Sstevel@tonic-gate * banks are all removed. 17711186Sayznaga * 17721186Sayznaga * The "delete" argument is 1 if this is called as a result of DDI_DETACH. In 17731186Sayznaga * this case, the DIMM data structures need to be deleted. The argument is 17741186Sayznaga * 0 if this called as a result of DDI_SUSPEND/DDI_RESUME. In this case, 17751186Sayznaga * the DIMM data structures are left alone. 17760Sstevel@tonic-gate */ 17770Sstevel@tonic-gate static void 17781186Sayznaga mlayout_del(int mc_id, int delete) 17790Sstevel@tonic-gate { 17800Sstevel@tonic-gate int i, j, dgrpid, devid, bankid, ndevgrps; 17810Sstevel@tonic-gate struct seg_info *seg; 17820Sstevel@tonic-gate struct bank_info *bank_curr; 17830Sstevel@tonic-gate struct mctrl_info *mctrl; 17840Sstevel@tonic-gate mc_dlist_t *dgrp_ptr; 17850Sstevel@tonic-gate mc_dlist_t *dev_ptr; 17860Sstevel@tonic-gate uint64_t base; 17870Sstevel@tonic-gate 17880Sstevel@tonic-gate mutex_enter(&mcdatamutex); 17890Sstevel@tonic-gate 17900Sstevel@tonic-gate /* delete mctrl_info */ 17910Sstevel@tonic-gate if ((mctrl = (struct mctrl_info *)mc_node_get(mc_id, mctrl_head)) != 17920Sstevel@tonic-gate NULL) { 17930Sstevel@tonic-gate ndevgrps = mctrl->ndevgrps; 17940Sstevel@tonic-gate mc_node_del((mc_dlist_t *)mctrl, &mctrl_head, &mctrl_tail); 17950Sstevel@tonic-gate kmem_free(mctrl, sizeof (struct mctrl_info)); 17960Sstevel@tonic-gate nmcs--; 17970Sstevel@tonic-gate 17980Sstevel@tonic-gate /* 17990Sstevel@tonic-gate * There is no other list left for disabled MC. 18000Sstevel@tonic-gate */ 18010Sstevel@tonic-gate if (ndevgrps == 0) { 18020Sstevel@tonic-gate mutex_exit(&mcdatamutex); 18030Sstevel@tonic-gate return; 18040Sstevel@tonic-gate } 18050Sstevel@tonic-gate } else 18060Sstevel@tonic-gate cmn_err(CE_WARN, "MC mlayout_del: mctrl is not found\n"); 18070Sstevel@tonic-gate 18080Sstevel@tonic-gate /* Delete device groups and devices of the detached MC */ 18090Sstevel@tonic-gate for (i = 0; i < NDGRPS; i++) { 18100Sstevel@tonic-gate dgrpid = mc_id * NDGRPS + i; 18110Sstevel@tonic-gate if (!(dgrp_ptr = mc_node_get(dgrpid, dgrp_head))) { 18120Sstevel@tonic-gate cmn_err(CE_WARN, "mlayout_del: no devgrp %d\n", dgrpid); 18130Sstevel@tonic-gate continue; 18140Sstevel@tonic-gate } 18150Sstevel@tonic-gate 18160Sstevel@tonic-gate for (j = 0; j < NDIMMS; j++) { 18170Sstevel@tonic-gate devid = dgrpid * NDIMMS + j; 18180Sstevel@tonic-gate if (dev_ptr = mc_node_get(devid, device_head)) { 18190Sstevel@tonic-gate mc_node_del(dev_ptr, &device_head, 18200Sstevel@tonic-gate &device_tail); 18210Sstevel@tonic-gate kmem_free(dev_ptr, sizeof (struct device_info)); 18220Sstevel@tonic-gate } else { 18230Sstevel@tonic-gate cmn_err(CE_WARN, "mlayout_del: no dev %d\n", 18240Sstevel@tonic-gate devid); 18250Sstevel@tonic-gate } 18260Sstevel@tonic-gate } 18270Sstevel@tonic-gate 18280Sstevel@tonic-gate mc_node_del(dgrp_ptr, &dgrp_head, &dgrp_tail); 18290Sstevel@tonic-gate kmem_free(dgrp_ptr, sizeof (struct dgrp_info)); 18300Sstevel@tonic-gate } 18310Sstevel@tonic-gate 18320Sstevel@tonic-gate /* Delete banks and segments if it has no bank */ 18330Sstevel@tonic-gate for (i = 0; i < NBANKS; i++) { 18340Sstevel@tonic-gate bankid = mc_id * NBANKS + i; 18350Sstevel@tonic-gate DPRINTF(MC_DESTRC_DEBUG, ("bank id %d\n", bankid)); 18360Sstevel@tonic-gate if (!(bank_curr = (struct bank_info *)mc_node_get(bankid, 18370Sstevel@tonic-gate bank_head))) { 18380Sstevel@tonic-gate cmn_err(CE_WARN, "mlayout_del: no bank %d\n", bankid); 18390Sstevel@tonic-gate continue; 18400Sstevel@tonic-gate } 18410Sstevel@tonic-gate 18420Sstevel@tonic-gate if (bank_curr->valid) { 18430Sstevel@tonic-gate base = bank_curr->um & ~bank_curr->uk; 18440Sstevel@tonic-gate base <<= MADR_UPA_SHIFT; 18450Sstevel@tonic-gate bank_curr->valid = 0; 18460Sstevel@tonic-gate memsize -= bank_curr->size; 18470Sstevel@tonic-gate 18480Sstevel@tonic-gate /* Delete bank at segment and segment if no bank left */ 18490Sstevel@tonic-gate if (!(seg = seg_match_base(base))) { 18500Sstevel@tonic-gate cmn_err(CE_WARN, "mlayout_del: no seg\n"); 18510Sstevel@tonic-gate mc_node_del((mc_dlist_t *)bank_curr, &bank_head, 18520Sstevel@tonic-gate &bank_tail); 18530Sstevel@tonic-gate kmem_free(bank_curr, sizeof (struct bank_info)); 18540Sstevel@tonic-gate continue; 18550Sstevel@tonic-gate } 18560Sstevel@tonic-gate 18570Sstevel@tonic-gate /* update the bank list at the segment */ 18580Sstevel@tonic-gate if (bank_curr->n_inseg == NULL) { 18590Sstevel@tonic-gate /* node is at the tail of list */ 18600Sstevel@tonic-gate seg->tb_inseg = bank_curr->p_inseg; 18610Sstevel@tonic-gate } else { 18620Sstevel@tonic-gate bank_curr->n_inseg->p_inseg = 18630Sstevel@tonic-gate bank_curr->p_inseg; 18640Sstevel@tonic-gate } 18650Sstevel@tonic-gate 18660Sstevel@tonic-gate if (bank_curr->p_inseg == NULL) { 18670Sstevel@tonic-gate /* node is at the head of list */ 18680Sstevel@tonic-gate seg->hb_inseg = bank_curr->n_inseg; 18690Sstevel@tonic-gate } else { 18700Sstevel@tonic-gate bank_curr->p_inseg->n_inseg = 18710Sstevel@tonic-gate bank_curr->n_inseg; 18720Sstevel@tonic-gate } 18730Sstevel@tonic-gate 18740Sstevel@tonic-gate seg->nbanks--; 18750Sstevel@tonic-gate seg->size -= bank_curr->size; 18760Sstevel@tonic-gate 18770Sstevel@tonic-gate if (seg->nbanks == 0) { 18780Sstevel@tonic-gate mc_node_del((mc_dlist_t *)seg, &seg_head, 18790Sstevel@tonic-gate &seg_tail); 18800Sstevel@tonic-gate kmem_free(seg, sizeof (struct seg_info)); 18810Sstevel@tonic-gate nsegments--; 18820Sstevel@tonic-gate } 18830Sstevel@tonic-gate 18840Sstevel@tonic-gate } 18850Sstevel@tonic-gate mc_node_del((mc_dlist_t *)bank_curr, &bank_head, &bank_tail); 18860Sstevel@tonic-gate kmem_free(bank_curr, sizeof (struct bank_info)); 18870Sstevel@tonic-gate } /* end of for loop for four banks */ 18880Sstevel@tonic-gate 18891186Sayznaga if (mc_dimm_sids && delete) { 18901186Sayznaga rw_enter(&mcdimmsids_rw, RW_WRITER); 18911186Sayznaga i = mc_get_sid_cache_index(mc_id); 18921186Sayznaga if (i >= 0) { 18931186Sayznaga mc_dimm_sids[i].state = MC_DIMM_SIDS_INVALID; 18941186Sayznaga if (mc_dimm_sids[i].sids) { 18951186Sayznaga kmem_free(mc_dimm_sids[i].sids, 18961186Sayznaga sizeof (dimm_sid_t) * (NDGRPS * NDIMMS)); 18971186Sayznaga mc_dimm_sids[i].sids = NULL; 18981186Sayznaga } 18991186Sayznaga } 19001186Sayznaga rw_exit(&mcdimmsids_rw); 19011186Sayznaga } 19021186Sayznaga 19030Sstevel@tonic-gate mutex_exit(&mcdatamutex); 19040Sstevel@tonic-gate } 19050Sstevel@tonic-gate 19060Sstevel@tonic-gate /* 19070Sstevel@tonic-gate * Search the segment in the list starting at seg_head by base address 19080Sstevel@tonic-gate * input: base address 19090Sstevel@tonic-gate * return: pointer of found segment or null if not found. 19100Sstevel@tonic-gate */ 19110Sstevel@tonic-gate static struct seg_info * 19120Sstevel@tonic-gate seg_match_base(u_longlong_t base) 19130Sstevel@tonic-gate { 19140Sstevel@tonic-gate static struct seg_info *seg_ptr; 19150Sstevel@tonic-gate 19160Sstevel@tonic-gate seg_ptr = (struct seg_info *)seg_head; 19170Sstevel@tonic-gate while (seg_ptr != NULL) { 1918946Smathue DPRINTF(MC_LIST_DEBUG, ("seg_match: base %lu,given base %llu\n", 19190Sstevel@tonic-gate seg_ptr->base, base)); 19200Sstevel@tonic-gate if (seg_ptr->base == base) 19210Sstevel@tonic-gate break; 19220Sstevel@tonic-gate seg_ptr = (struct seg_info *)seg_ptr->seg_node.next; 19230Sstevel@tonic-gate } 19240Sstevel@tonic-gate return (seg_ptr); 19250Sstevel@tonic-gate } 19260Sstevel@tonic-gate 19270Sstevel@tonic-gate /* 19280Sstevel@tonic-gate * mc_dlist is a double linking list, including unique id, and pointers to 19290Sstevel@tonic-gate * next, and previous nodes. seg_info, bank_info, dgrp_info, device_info, 19300Sstevel@tonic-gate * and mctrl_info has it at the top to share the operations, add, del, and get. 19310Sstevel@tonic-gate * 19320Sstevel@tonic-gate * The new node is added at the tail and is not sorted. 19330Sstevel@tonic-gate * 19340Sstevel@tonic-gate * Input: The pointer of node to be added, head and tail of the list 19350Sstevel@tonic-gate */ 19360Sstevel@tonic-gate 19370Sstevel@tonic-gate static void 19380Sstevel@tonic-gate mc_node_add(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail) 19390Sstevel@tonic-gate { 19400Sstevel@tonic-gate DPRINTF(MC_LIST_DEBUG, ("mc_node_add: node->id %d head %p tail %p\n", 19410Sstevel@tonic-gate node->id, *head, *tail)); 19420Sstevel@tonic-gate 19430Sstevel@tonic-gate if (*head != NULL) { 19440Sstevel@tonic-gate node->prev = *tail; 19450Sstevel@tonic-gate node->next = (*tail)->next; 19460Sstevel@tonic-gate (*tail)->next = node; 19470Sstevel@tonic-gate *tail = node; 19480Sstevel@tonic-gate } else { 19490Sstevel@tonic-gate node->next = node->prev = NULL; 19500Sstevel@tonic-gate *head = *tail = node; 19510Sstevel@tonic-gate } 19520Sstevel@tonic-gate } 19530Sstevel@tonic-gate 19540Sstevel@tonic-gate /* 19550Sstevel@tonic-gate * Input: The pointer of node to be deleted, head and tail of the list 19560Sstevel@tonic-gate * 19570Sstevel@tonic-gate * Deleted node will be at the following positions 19580Sstevel@tonic-gate * 1. At the tail of the list 19590Sstevel@tonic-gate * 2. At the head of the list 19600Sstevel@tonic-gate * 3. At the head and tail of the list, i.e. only one left. 19610Sstevel@tonic-gate * 4. At the middle of the list 19620Sstevel@tonic-gate */ 19630Sstevel@tonic-gate 19640Sstevel@tonic-gate static void 19650Sstevel@tonic-gate mc_node_del(mc_dlist_t *node, mc_dlist_t **head, mc_dlist_t **tail) 19660Sstevel@tonic-gate { 19670Sstevel@tonic-gate if (node->next == NULL) { 19680Sstevel@tonic-gate /* deleted node is at the tail of list */ 19690Sstevel@tonic-gate *tail = node->prev; 19700Sstevel@tonic-gate } else { 19710Sstevel@tonic-gate node->next->prev = node->prev; 19720Sstevel@tonic-gate } 19730Sstevel@tonic-gate 19740Sstevel@tonic-gate if (node->prev == NULL) { 19750Sstevel@tonic-gate /* deleted node is at the head of list */ 19760Sstevel@tonic-gate *head = node->next; 19770Sstevel@tonic-gate } else { 19780Sstevel@tonic-gate node->prev->next = node->next; 19790Sstevel@tonic-gate } 19800Sstevel@tonic-gate } 19810Sstevel@tonic-gate 19820Sstevel@tonic-gate /* 19830Sstevel@tonic-gate * Search the list from the head of the list to match the given id 19840Sstevel@tonic-gate * Input: id and the head of the list 19850Sstevel@tonic-gate * Return: pointer of found node 19860Sstevel@tonic-gate */ 19870Sstevel@tonic-gate static mc_dlist_t * 19880Sstevel@tonic-gate mc_node_get(int id, mc_dlist_t *head) 19890Sstevel@tonic-gate { 19900Sstevel@tonic-gate mc_dlist_t *node; 19910Sstevel@tonic-gate 19920Sstevel@tonic-gate node = head; 19930Sstevel@tonic-gate while (node != NULL) { 19940Sstevel@tonic-gate DPRINTF(MC_LIST_DEBUG, ("mc_node_get: id %d, given id %d\n", 19950Sstevel@tonic-gate node->id, id)); 19960Sstevel@tonic-gate if (node->id == id) 19970Sstevel@tonic-gate break; 19980Sstevel@tonic-gate node = node->next; 19990Sstevel@tonic-gate } 20000Sstevel@tonic-gate return (node); 20010Sstevel@tonic-gate } 20020Sstevel@tonic-gate 20030Sstevel@tonic-gate /* 20040Sstevel@tonic-gate * mc-us3 driver allows a platform to add extra label 20050Sstevel@tonic-gate * information to the unum string. If a platform implements a 20060Sstevel@tonic-gate * kernel function called plat_add_mem_unum_label() it will be 20070Sstevel@tonic-gate * executed. This would typically be implemented in the platmod. 20080Sstevel@tonic-gate */ 20090Sstevel@tonic-gate static void 20100Sstevel@tonic-gate mc_add_mem_unum_label(char *buf, int mcid, int bank, int dimm) 20110Sstevel@tonic-gate { 20120Sstevel@tonic-gate if (&plat_add_mem_unum_label) 20130Sstevel@tonic-gate plat_add_mem_unum_label(buf, mcid, bank, dimm); 20140Sstevel@tonic-gate } 20151186Sayznaga 20161186Sayznaga static int 20171186Sayznaga mc_get_sid_cache_index(int mcid) 20181186Sayznaga { 20191186Sayznaga int i; 20201186Sayznaga 20211186Sayznaga for (i = 0; i < max_entries; i++) { 20221186Sayznaga if (mcid == mc_dimm_sids[i].mcid) 20231186Sayznaga return (i); 20241186Sayznaga } 20251186Sayznaga 20261186Sayznaga return (-1); 20271186Sayznaga } 20281186Sayznaga 2029*1463Sayznaga static void 2030*1463Sayznaga mc_update_bank(struct bank_info *bank) 2031*1463Sayznaga { 2032*1463Sayznaga int i, j; 2033*1463Sayznaga int bankid, mcid, dgrp_no; 2034*1463Sayznaga 2035*1463Sayznaga /* 2036*1463Sayznaga * Mark the MC if DIMM sids are not available. 2037*1463Sayznaga * Mark which segment the DIMMs belong to. Allocate 2038*1463Sayznaga * space to store DIMM serial ids which are later 2039*1463Sayznaga * provided by the platform layer, and update the bank_info 2040*1463Sayznaga * structure with pointers to its serial ids. 2041*1463Sayznaga */ 2042*1463Sayznaga bankid = bank->bank_node.id; 2043*1463Sayznaga mcid = bankid / NBANKS; 2044*1463Sayznaga i = mc_get_sid_cache_index(mcid); 2045*1463Sayznaga if (mc_dimm_sids[i].state == MC_DIMM_SIDS_INVALID) 2046*1463Sayznaga mc_dimm_sids[i].state = MC_DIMM_SIDS_REQUESTED; 2047*1463Sayznaga 2048*1463Sayznaga mc_dimm_sids[i].seg_id = bank->seg_id; 2049*1463Sayznaga 2050*1463Sayznaga if (mc_dimm_sids[i].sids == NULL) { 2051*1463Sayznaga mc_dimm_sids[i].sids = (dimm_sid_t *)kmem_zalloc( 2052*1463Sayznaga sizeof (dimm_sid_t) * (NDGRPS * NDIMMS), KM_SLEEP); 2053*1463Sayznaga } 2054*1463Sayznaga 2055*1463Sayznaga dgrp_no = bank->devgrp_id % NDGRPS; 2056*1463Sayznaga 2057*1463Sayznaga for (j = 0; j < NDIMMS; j++) { 2058*1463Sayznaga bank->dimmsidp[j] = 2059*1463Sayznaga &mc_dimm_sids[i].sids[j + (NDIMMS * dgrp_no)]; 2060*1463Sayznaga } 2061*1463Sayznaga } 2062*1463Sayznaga 20631186Sayznaga static int 20641186Sayznaga mc_populate_sid_cache(void) 20651186Sayznaga { 20661186Sayznaga struct bank_info *bank; 20671186Sayznaga 20681186Sayznaga if (&plat_populate_sid_cache == 0) 20691186Sayznaga return (ENOTSUP); 20701186Sayznaga 20711186Sayznaga ASSERT(RW_WRITE_HELD(&mcdimmsids_rw)); 20721186Sayznaga 20731186Sayznaga bank = (struct bank_info *)bank_head; 20741186Sayznaga while (bank != NULL) { 20751186Sayznaga if (!bank->valid) { 20761186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 20771186Sayznaga continue; 20781186Sayznaga } 20791186Sayznaga 2080*1463Sayznaga mc_update_bank(bank); 20811186Sayznaga 20821186Sayznaga bank = (struct bank_info *)bank->bank_node.next; 20831186Sayznaga } 20841186Sayznaga 20851186Sayznaga 20861186Sayznaga /* 20871186Sayznaga * Call to the platform layer to populate the cache 20881186Sayznaga * with DIMM serial ids. 20891186Sayznaga */ 20901186Sayznaga return (plat_populate_sid_cache(mc_dimm_sids, max_entries)); 20911186Sayznaga } 20921186Sayznaga 20931186Sayznaga static void 20941186Sayznaga mc_init_sid_cache_thr(void) 20951186Sayznaga { 20961186Sayznaga ASSERT(mc_dimm_sids == NULL); 20971186Sayznaga 20981186Sayznaga mutex_enter(&mcdatamutex); 20991186Sayznaga rw_enter(&mcdimmsids_rw, RW_WRITER); 21001186Sayznaga 21011186Sayznaga mc_dimm_sids = plat_alloc_sid_cache(&max_entries); 21021186Sayznaga (void) mc_populate_sid_cache(); 21031186Sayznaga 21041186Sayznaga rw_exit(&mcdimmsids_rw); 21051186Sayznaga mutex_exit(&mcdatamutex); 21061186Sayznaga } 21071186Sayznaga 21081186Sayznaga static int 21091186Sayznaga mc_init_sid_cache(void) 21101186Sayznaga { 21111186Sayznaga if (&plat_alloc_sid_cache) { 21121186Sayznaga (void) thread_create(NULL, 0, mc_init_sid_cache_thr, NULL, 0, 21131186Sayznaga &p0, TS_RUN, minclsyspri); 21141186Sayznaga return (0); 21151186Sayznaga } else 21161186Sayznaga return (ENOTSUP); 21171186Sayznaga } 21181186Sayznaga 21191186Sayznaga static int 21201186Sayznaga mc_get_mem_sid(int mcid, int dimm, char *buf, int buflen, int *lenp) 21211186Sayznaga { 21221186Sayznaga int i; 21231186Sayznaga 21241186Sayznaga if (buflen < DIMM_SERIAL_ID_LEN) 21251186Sayznaga return (ENOSPC); 21261186Sayznaga 21271186Sayznaga /* 21281186Sayznaga * If DIMM serial ids have not been cached yet, tell the 21291186Sayznaga * caller to try again. 21301186Sayznaga */ 21311186Sayznaga if (!rw_tryenter(&mcdimmsids_rw, RW_READER)) 21321186Sayznaga return (EAGAIN); 21331186Sayznaga 21341186Sayznaga if (mc_dimm_sids == NULL) { 21351186Sayznaga rw_exit(&mcdimmsids_rw); 21361186Sayznaga return (EAGAIN); 21371186Sayznaga } 21381186Sayznaga 21391186Sayznaga /* 21401186Sayznaga * Find dimm serial id using mcid and dimm # 21411186Sayznaga */ 21421186Sayznaga for (i = 0; i < max_entries; i++) { 21431186Sayznaga if (mc_dimm_sids[i].mcid == mcid) 21441186Sayznaga break; 21451186Sayznaga } 21461186Sayznaga if ((i == max_entries) || (!mc_dimm_sids[i].sids)) { 21471186Sayznaga rw_exit(&mcdimmsids_rw); 21481186Sayznaga return (ENOENT); 21491186Sayznaga } 21501186Sayznaga 21511186Sayznaga (void) strlcpy(buf, mc_dimm_sids[i].sids[dimm], 21521186Sayznaga DIMM_SERIAL_ID_LEN); 21531186Sayznaga *lenp = strlen(buf); 21541186Sayznaga 21551186Sayznaga rw_exit(&mcdimmsids_rw); 21561186Sayznaga return (0); 21571186Sayznaga } 2158