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