120c794b3Sgavinm /* 220c794b3Sgavinm * CDDL HEADER START 320c794b3Sgavinm * 420c794b3Sgavinm * The contents of this file are subject to the terms of the 520c794b3Sgavinm * Common Development and Distribution License (the "License"). 620c794b3Sgavinm * You may not use this file except in compliance with the License. 720c794b3Sgavinm * 820c794b3Sgavinm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 920c794b3Sgavinm * or http://www.opensolaris.org/os/licensing. 1020c794b3Sgavinm * See the License for the specific language governing permissions 1120c794b3Sgavinm * and limitations under the License. 1220c794b3Sgavinm * 1320c794b3Sgavinm * When distributing Covered Code, include this CDDL HEADER in each 1420c794b3Sgavinm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1520c794b3Sgavinm * If applicable, add the following below this CDDL HEADER, with the 1620c794b3Sgavinm * fields enclosed by brackets "[]" replaced with your own identifying 1720c794b3Sgavinm * information: Portions Copyright [yyyy] [name of copyright owner] 1820c794b3Sgavinm * 1920c794b3Sgavinm * CDDL HEADER END 2020c794b3Sgavinm */ 2120c794b3Sgavinm 2220c794b3Sgavinm /* 239ff4cbe7SAdrian Frost * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2420c794b3Sgavinm * Use is subject to license terms. 2589b43686SBayard Bell * Copyright (c) 2011 Bayard G. Bell. All rights reserved. 2620c794b3Sgavinm */ 2720c794b3Sgavinm 2820c794b3Sgavinm #include <sys/types.h> 2920c794b3Sgavinm #include <sys/time.h> 3020c794b3Sgavinm #include <sys/nvpair.h> 3120c794b3Sgavinm #include <sys/cmn_err.h> 3220c794b3Sgavinm #include <sys/cred.h> 3320c794b3Sgavinm #include <sys/open.h> 3420c794b3Sgavinm #include <sys/ddi.h> 3520c794b3Sgavinm #include <sys/sunddi.h> 3620c794b3Sgavinm #include <sys/conf.h> 3720c794b3Sgavinm #include <sys/modctl.h> 3820c794b3Sgavinm #include <sys/cyclic.h> 3920c794b3Sgavinm #include <sys/errorq.h> 4020c794b3Sgavinm #include <sys/stat.h> 4120c794b3Sgavinm #include <sys/cpuvar.h> 4220c794b3Sgavinm #include <sys/mc_intel.h> 4320c794b3Sgavinm #include <sys/mc.h> 4420c794b3Sgavinm #include <sys/fm/protocol.h> 4520c794b3Sgavinm #include "nb_log.h" 4620c794b3Sgavinm #include "nb5000.h" 4720c794b3Sgavinm 4820c794b3Sgavinm nvlist_t *inb_mc_nvl; 4920c794b3Sgavinm krwlock_t inb_mc_lock; 5020c794b3Sgavinm 5120c794b3Sgavinm char *inb_mc_snapshot; 5220c794b3Sgavinm uint_t nb_config_gen; 5320c794b3Sgavinm uint_t inb_mc_snapshotgen; 5420c794b3Sgavinm size_t inb_mc_snapshotsz; 5520c794b3Sgavinm static dev_info_t *inb_dip; 5620c794b3Sgavinm int nb_allow_detach = 0; 5730cfc677SAdrian Frost int nb_no_smbios; 5820c794b3Sgavinm 5920c794b3Sgavinm static uint64_t 6020c794b3Sgavinm rank_to_base(uint8_t branch, uint8_t rank, uint8_t *interleave, uint64_t *limit, 6120c794b3Sgavinm uint64_t *hole_base, uint64_t *hole_size, uint8_t *wayp, 6220c794b3Sgavinm uint8_t *branch_interleavep) 6320c794b3Sgavinm { 6420c794b3Sgavinm uint8_t i, j; 6520c794b3Sgavinm uint64_t base = 0; 6620c794b3Sgavinm uint64_t lt = 0; 6720c794b3Sgavinm uint64_t h = 0; 6820c794b3Sgavinm uint64_t hs = 0; 6920c794b3Sgavinm uint8_t il = 1; 7020c794b3Sgavinm uint8_t way = 0; 7120c794b3Sgavinm uint8_t branch_interleave = 0; 7220c794b3Sgavinm 7320c794b3Sgavinm for (i = 0; i < NB_MEM_RANK_SELECT; i++) { 7420c794b3Sgavinm for (j = 0; j < NB_RANKS_IN_SELECT; j++) { 7520c794b3Sgavinm if (nb_ranks[branch][i].rank[j] == rank) { 7620c794b3Sgavinm base = nb_ranks[branch][i].base; 7720c794b3Sgavinm lt = nb_ranks[branch][i].limit; 7820c794b3Sgavinm il = nb_ranks[branch][i].interleave; 7920c794b3Sgavinm h = nb_ranks[branch][i].hole_base; 8020c794b3Sgavinm hs = nb_ranks[branch][i].hole_size; 8120c794b3Sgavinm way = j; 8220c794b3Sgavinm branch_interleave = 8320c794b3Sgavinm nb_ranks[branch][i].branch_interleave; 8420c794b3Sgavinm i = NB_MEM_RANK_SELECT; 8520c794b3Sgavinm break; 8620c794b3Sgavinm } 8720c794b3Sgavinm } 8820c794b3Sgavinm } 8920c794b3Sgavinm if (lt == 0) { 9020c794b3Sgavinm for (i = 0; lt == 0 && i < NB_MEM_BRANCH_SELECT; i++) { 9120c794b3Sgavinm if (nb_banks[i].way[branch] && 9220c794b3Sgavinm base >= nb_banks[i].base && 9320c794b3Sgavinm base < nb_banks[i].base + nb_banks[i].limit) { 9420c794b3Sgavinm lt = nb_banks[i].limit; 9520c794b3Sgavinm break; 9620c794b3Sgavinm } 9720c794b3Sgavinm } 9820c794b3Sgavinm } 9920c794b3Sgavinm *interleave = il; 10020c794b3Sgavinm *limit = lt; 10120c794b3Sgavinm *hole_base = h; 10220c794b3Sgavinm *hole_size = hs; 10320c794b3Sgavinm *wayp = way; 10420c794b3Sgavinm *branch_interleavep = branch_interleave; 10520c794b3Sgavinm return (base); 10620c794b3Sgavinm } 10720c794b3Sgavinm 10885738508SVuong Nguyen /*ARGSUSED*/ 10920c794b3Sgavinm void 11020c794b3Sgavinm inb_rank(nvlist_t *newdimm, nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm) 11120c794b3Sgavinm { 11220c794b3Sgavinm nvlist_t **newrank; 11320c794b3Sgavinm int i; 11420c794b3Sgavinm 11520c794b3Sgavinm newrank = kmem_zalloc(sizeof (nvlist_t *) * nb_dimm->nranks, KM_SLEEP); 11620c794b3Sgavinm for (i = 0; i < nb_dimm->nranks; i++) { 11720c794b3Sgavinm uint64_t dimm_base; 11820c794b3Sgavinm uint64_t limit; 11920c794b3Sgavinm uint8_t interleave; 12020c794b3Sgavinm uint8_t way; 12120c794b3Sgavinm uint8_t branch_interleave; 12220c794b3Sgavinm uint64_t hole_base; 12320c794b3Sgavinm uint64_t hole_size; 12420c794b3Sgavinm 12585738508SVuong Nguyen dimm_base = rank_to_base(channel/nb_channels_per_branch, 12685738508SVuong Nguyen nb_dimm->start_rank + i, &interleave, 12720c794b3Sgavinm &limit, &hole_base, &hole_size, &way, &branch_interleave); 12820c794b3Sgavinm (void) nvlist_alloc(&newrank[i], NV_UNIQUE_NAME, KM_SLEEP); 12920c794b3Sgavinm 13020c794b3Sgavinm (void) nvlist_add_uint64(newrank[i], "dimm-rank-base", 13120c794b3Sgavinm dimm_base); 13220c794b3Sgavinm if (hole_size) { 13320c794b3Sgavinm (void) nvlist_add_uint64(newrank[i], "dimm-hole", 13420c794b3Sgavinm hole_base); 13520c794b3Sgavinm (void) nvlist_add_uint64(newrank[i], "dimm-hole-size", 13620c794b3Sgavinm hole_size); 13720c794b3Sgavinm } 13820c794b3Sgavinm (void) nvlist_add_uint64(newrank[i], "dimm-rank-limit", 13920c794b3Sgavinm limit); 14020c794b3Sgavinm if (interleave > 1) { 14120c794b3Sgavinm (void) nvlist_add_uint32(newrank[i], 14220c794b3Sgavinm "dimm-rank-interleave", (uint32_t)interleave); 14320c794b3Sgavinm (void) nvlist_add_uint32(newrank[i], 14420c794b3Sgavinm "dimm-rank-interleave-way", (uint32_t)way); 14520c794b3Sgavinm if (branch_interleave) { 14620c794b3Sgavinm (void) nvlist_add_uint32(newrank[i], 14720c794b3Sgavinm "dimm-rank-interleave-branch", (uint32_t)1); 14820c794b3Sgavinm } 14920c794b3Sgavinm } 15020c794b3Sgavinm } 15120c794b3Sgavinm (void) nvlist_add_nvlist_array(newdimm, MCINTEL_NVLIST_RANKS, newrank, 15220c794b3Sgavinm nb_dimm->nranks); 153e46e4715Saf for (i = 0; i < nb_dimm->nranks; i++) 154e46e4715Saf nvlist_free(newrank[i]); 15520c794b3Sgavinm kmem_free(newrank, sizeof (nvlist_t *) * nb_dimm->nranks); 15620c794b3Sgavinm } 15720c794b3Sgavinm 15820c794b3Sgavinm nvlist_t * 15920c794b3Sgavinm inb_dimm(nb_dimm_t *nb_dimm, uint8_t channel, uint32_t dimm) 16020c794b3Sgavinm { 16120c794b3Sgavinm nvlist_t *newdimm; 16220c794b3Sgavinm uint8_t t; 16320c794b3Sgavinm char sbuf[65]; 16420c794b3Sgavinm 16520c794b3Sgavinm (void) nvlist_alloc(&newdimm, NV_UNIQUE_NAME, KM_SLEEP); 16620c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "dimm-number", dimm); 16720c794b3Sgavinm 16820c794b3Sgavinm if (nb_dimm->dimm_size >= 1024*1024*1024) { 16920c794b3Sgavinm (void) snprintf(sbuf, sizeof (sbuf), "%dG", 17020c794b3Sgavinm (int)(nb_dimm->dimm_size / (1024*1024*1024))); 17120c794b3Sgavinm } else { 17220c794b3Sgavinm (void) snprintf(sbuf, sizeof (sbuf), "%dM", 17320c794b3Sgavinm (int)(nb_dimm->dimm_size / (1024*1024))); 17420c794b3Sgavinm } 17520c794b3Sgavinm (void) nvlist_add_string(newdimm, "dimm-size", sbuf); 1765f28a827Saf (void) nvlist_add_uint64(newdimm, "size", nb_dimm->dimm_size); 17720c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "nbanks", (uint32_t)nb_dimm->nbanks); 17820c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "ncolumn", 17920c794b3Sgavinm (uint32_t)nb_dimm->ncolumn); 18020c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "nrow", (uint32_t)nb_dimm->nrow); 18120c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "width", (uint32_t)nb_dimm->width); 18285738508SVuong Nguyen (void) nvlist_add_int32(newdimm, MCINTEL_NVLIST_1ST_RANK, 18385738508SVuong Nguyen (int32_t)nb_dimm->start_rank); 18420c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "ranks", (uint32_t)nb_dimm->nranks); 18520c794b3Sgavinm inb_rank(newdimm, nb_dimm, channel, dimm); 18620c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "manufacture-id", 18720c794b3Sgavinm (uint32_t)nb_dimm->manufacture_id); 18820c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "manufacture-location", 18920c794b3Sgavinm (uint32_t)nb_dimm->manufacture_location); 19020c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "manufacture-week", 19120c794b3Sgavinm (uint32_t)nb_dimm->manufacture_week); 19220c794b3Sgavinm (void) nvlist_add_uint32(newdimm, "manufacture-year", 19320c794b3Sgavinm (uint32_t)nb_dimm->manufacture_year + 2000); 19420c794b3Sgavinm /* create Sun Serial number from SPD data */ 19520c794b3Sgavinm (void) snprintf(sbuf, sizeof (sbuf), "%04x%02x%02x%02x%08x", 19620c794b3Sgavinm (uint32_t)nb_dimm->manufacture_id & 0x7fff, 19720c794b3Sgavinm (uint32_t)nb_dimm->manufacture_location, 19820c794b3Sgavinm (uint32_t)nb_dimm->manufacture_year, 19920c794b3Sgavinm (uint32_t)nb_dimm->manufacture_week, 20020c794b3Sgavinm nb_dimm->serial_number); 20120c794b3Sgavinm (void) nvlist_add_string(newdimm, FM_FMRI_HC_SERIAL_ID, sbuf); 20220c794b3Sgavinm if (nb_dimm->part_number && nb_dimm->part_number[0]) { 20320c794b3Sgavinm t = sizeof (nb_dimm->part_number); 20420c794b3Sgavinm (void) strncpy(sbuf, nb_dimm->part_number, t); 20520c794b3Sgavinm sbuf[t] = 0; 20620c794b3Sgavinm (void) nvlist_add_string(newdimm, FM_FMRI_HC_PART, sbuf); 20720c794b3Sgavinm } 20820c794b3Sgavinm if (nb_dimm->revision && nb_dimm->revision[0]) { 20920c794b3Sgavinm t = sizeof (nb_dimm->revision); 21020c794b3Sgavinm (void) strncpy(sbuf, nb_dimm->revision, t); 21120c794b3Sgavinm sbuf[t] = 0; 21220c794b3Sgavinm (void) nvlist_add_string(newdimm, FM_FMRI_HC_REVISION, sbuf); 21320c794b3Sgavinm } 21420c794b3Sgavinm t = sizeof (nb_dimm->label); 21520c794b3Sgavinm (void) strncpy(sbuf, nb_dimm->label, t); 21620c794b3Sgavinm sbuf[t] = 0; 21720c794b3Sgavinm (void) nvlist_add_string(newdimm, FM_FAULT_FRU_LABEL, sbuf); 21820c794b3Sgavinm return (newdimm); 21920c794b3Sgavinm } 22020c794b3Sgavinm 22120c794b3Sgavinm static void 22220c794b3Sgavinm inb_dimmlist(nvlist_t *nvl) 22320c794b3Sgavinm { 22420c794b3Sgavinm nvlist_t **dimmlist; 22520c794b3Sgavinm nvlist_t **newchannel; 22685738508SVuong Nguyen int nchannels = nb_number_memory_controllers * nb_channels_per_branch; 22720c794b3Sgavinm int nd; 22820c794b3Sgavinm uint8_t i, j; 22920c794b3Sgavinm nb_dimm_t **dimmpp; 23020c794b3Sgavinm nb_dimm_t *dimmp; 23120c794b3Sgavinm 23220c794b3Sgavinm dimmlist = kmem_zalloc(sizeof (nvlist_t *) * nb_dimms_per_channel, 23320c794b3Sgavinm KM_SLEEP); 23420c794b3Sgavinm newchannel = kmem_zalloc(sizeof (nvlist_t *) * nchannels, KM_SLEEP); 23520c794b3Sgavinm dimmpp = nb_dimms; 23620c794b3Sgavinm for (i = 0; i < nchannels; i++) { 23720c794b3Sgavinm (void) nvlist_alloc(&newchannel[i], NV_UNIQUE_NAME, KM_SLEEP); 23820c794b3Sgavinm nd = 0; 23920c794b3Sgavinm for (j = 0; j < nb_dimms_per_channel; j++) { 24020c794b3Sgavinm dimmp = *dimmpp; 24120c794b3Sgavinm if (dimmp != NULL) { 24220c794b3Sgavinm dimmlist[nd] = inb_dimm(dimmp, i, (uint32_t)j); 24320c794b3Sgavinm nd++; 24420c794b3Sgavinm } 24520c794b3Sgavinm dimmpp++; 24620c794b3Sgavinm } 24720c794b3Sgavinm if (nd) { 24820c794b3Sgavinm (void) nvlist_add_nvlist_array(newchannel[i], 24920c794b3Sgavinm "memory-dimms", dimmlist, nd); 25020c794b3Sgavinm for (j = 0; j < nd; j++) 25120c794b3Sgavinm nvlist_free(dimmlist[j]); 25220c794b3Sgavinm } 25320c794b3Sgavinm } 25420c794b3Sgavinm (void) nvlist_add_nvlist_array(nvl, MCINTEL_NVLIST_MC, newchannel, 25520c794b3Sgavinm nchannels); 256e46e4715Saf for (i = 0; i < nchannels; i++) 257e46e4715Saf nvlist_free(newchannel[i]); 25820c794b3Sgavinm kmem_free(dimmlist, sizeof (nvlist_t *) * nb_dimms_per_channel); 25920c794b3Sgavinm kmem_free(newchannel, sizeof (nvlist_t *) * nchannels); 26020c794b3Sgavinm } 26120c794b3Sgavinm 26220c794b3Sgavinm static char * 26320c794b3Sgavinm inb_mc_name() 26420c794b3Sgavinm { 26520c794b3Sgavinm char *mc; 26620c794b3Sgavinm 26720c794b3Sgavinm switch (nb_chipset) { 26820c794b3Sgavinm case INTEL_NB_7300: 26920c794b3Sgavinm mc = "Intel 7300"; 27020c794b3Sgavinm break; 2715f28a827Saf case INTEL_NB_5400: 2725f28a827Saf mc = "Intel 5400"; 2735f28a827Saf break; 2745f28a827Saf case INTEL_NB_5400A: 2755f28a827Saf mc = "Intel 5400A"; 2765f28a827Saf break; 2775f28a827Saf case INTEL_NB_5400B: 2785f28a827Saf mc = "Intel 5400B"; 2795f28a827Saf break; 28085738508SVuong Nguyen case INTEL_NB_5100: 28185738508SVuong Nguyen mc = "Intel 5100"; 28285738508SVuong Nguyen break; 28320c794b3Sgavinm case INTEL_NB_5000P: 28420c794b3Sgavinm mc = "Intel 5000P"; 28520c794b3Sgavinm break; 28620c794b3Sgavinm case INTEL_NB_5000V: 28720c794b3Sgavinm mc = "Intel 5000V"; 28820c794b3Sgavinm break; 28920c794b3Sgavinm case INTEL_NB_5000X: 29020c794b3Sgavinm mc = "Intel 5000X"; 29120c794b3Sgavinm break; 29220c794b3Sgavinm case INTEL_NB_5000Z: 29320c794b3Sgavinm mc = "Intel 5000Z"; 29420c794b3Sgavinm break; 29520c794b3Sgavinm default: 29620c794b3Sgavinm mc = "Intel 5000"; 29720c794b3Sgavinm break; 29820c794b3Sgavinm } 29920c794b3Sgavinm return (mc); 30020c794b3Sgavinm } 30120c794b3Sgavinm 30220c794b3Sgavinm static void 30320c794b3Sgavinm inb_create_nvl() 30420c794b3Sgavinm { 30520c794b3Sgavinm nvlist_t *nvl; 30620c794b3Sgavinm 30720c794b3Sgavinm (void) nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 30820c794b3Sgavinm (void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_VERSTR, 30920c794b3Sgavinm MCINTEL_NVLIST_VERS); 31020c794b3Sgavinm (void) nvlist_add_string(nvl, "memory-controller", inb_mc_name()); 31185738508SVuong Nguyen if (nb_chipset == INTEL_NB_5100) 31285738508SVuong Nguyen (void) nvlist_add_uint8(nvl, MCINTEL_NVLIST_NMEM, 31385738508SVuong Nguyen (uint8_t)nb_number_memory_controllers); 31420c794b3Sgavinm inb_dimmlist(nvl); 31520c794b3Sgavinm 316*aab83bb8SJosef 'Jeff' Sipek nvlist_free(inb_mc_nvl); 31720c794b3Sgavinm inb_mc_nvl = nvl; 31820c794b3Sgavinm } 31920c794b3Sgavinm 32020c794b3Sgavinm static void 32120c794b3Sgavinm inb_mc_snapshot_destroy() 32220c794b3Sgavinm { 32320c794b3Sgavinm ASSERT(RW_LOCK_HELD(&inb_mc_lock)); 32420c794b3Sgavinm 32520c794b3Sgavinm if (inb_mc_snapshot == NULL) 32620c794b3Sgavinm return; 32720c794b3Sgavinm 32820c794b3Sgavinm kmem_free(inb_mc_snapshot, inb_mc_snapshotsz); 32920c794b3Sgavinm inb_mc_snapshot = NULL; 33020c794b3Sgavinm inb_mc_snapshotsz = 0; 33120c794b3Sgavinm inb_mc_snapshotgen++; 33220c794b3Sgavinm } 33320c794b3Sgavinm 33420c794b3Sgavinm static int 33520c794b3Sgavinm inb_mc_snapshot_update() 33620c794b3Sgavinm { 33720c794b3Sgavinm ASSERT(RW_LOCK_HELD(&inb_mc_lock)); 33820c794b3Sgavinm 33920c794b3Sgavinm if (inb_mc_snapshot != NULL) 34020c794b3Sgavinm return (0); 34120c794b3Sgavinm 34220c794b3Sgavinm if (nvlist_pack(inb_mc_nvl, &inb_mc_snapshot, &inb_mc_snapshotsz, 34320c794b3Sgavinm NV_ENCODE_XDR, KM_SLEEP) != 0) 34420c794b3Sgavinm return (-1); 34520c794b3Sgavinm 34620c794b3Sgavinm return (0); 34720c794b3Sgavinm } 34820c794b3Sgavinm 34920c794b3Sgavinm /*ARGSUSED*/ 35020c794b3Sgavinm static int 35120c794b3Sgavinm inb_mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 35220c794b3Sgavinm int *rvalp) 35320c794b3Sgavinm { 35420c794b3Sgavinm int rc = 0; 35520c794b3Sgavinm mc_snapshot_info_t mcs; 35620c794b3Sgavinm 35720c794b3Sgavinm if (cmd != MC_IOC_SNAPSHOT_INFO && cmd != MC_IOC_SNAPSHOT) 35820c794b3Sgavinm return (EINVAL); 35920c794b3Sgavinm 36020c794b3Sgavinm rw_enter(&inb_mc_lock, RW_READER); 36120c794b3Sgavinm if (inb_mc_nvl == NULL || inb_mc_snapshotgen != nb_config_gen) { 36220c794b3Sgavinm if (!rw_tryupgrade(&inb_mc_lock)) { 36320c794b3Sgavinm rw_exit(&inb_mc_lock); 36420c794b3Sgavinm return (EAGAIN); 36520c794b3Sgavinm } 36620c794b3Sgavinm if (inb_mc_nvl) 36720c794b3Sgavinm inb_mc_snapshot_destroy(); 36820c794b3Sgavinm inb_create_nvl(); 36920c794b3Sgavinm nb_config_gen = inb_mc_snapshotgen; 37020c794b3Sgavinm (void) inb_mc_snapshot_update(); 37120c794b3Sgavinm } 37220c794b3Sgavinm switch (cmd) { 37320c794b3Sgavinm case MC_IOC_SNAPSHOT_INFO: 37420c794b3Sgavinm mcs.mcs_size = (uint32_t)inb_mc_snapshotsz; 37520c794b3Sgavinm mcs.mcs_gen = inb_mc_snapshotgen; 37620c794b3Sgavinm 37720c794b3Sgavinm if (ddi_copyout(&mcs, (void *)arg, sizeof (mc_snapshot_info_t), 37820c794b3Sgavinm mode) < 0) 37920c794b3Sgavinm rc = EFAULT; 38020c794b3Sgavinm break; 38120c794b3Sgavinm case MC_IOC_SNAPSHOT: 38220c794b3Sgavinm if (ddi_copyout(inb_mc_snapshot, (void *)arg, inb_mc_snapshotsz, 38320c794b3Sgavinm mode) < 0) 38420c794b3Sgavinm rc = EFAULT; 38520c794b3Sgavinm break; 38620c794b3Sgavinm } 38720c794b3Sgavinm rw_exit(&inb_mc_lock); 38820c794b3Sgavinm return (rc); 38920c794b3Sgavinm } 39020c794b3Sgavinm 39120c794b3Sgavinm /*ARGSUSED*/ 39220c794b3Sgavinm static int 39320c794b3Sgavinm inb_mc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 39420c794b3Sgavinm void **result) 39520c794b3Sgavinm { 39620c794b3Sgavinm if ((infocmd != DDI_INFO_DEVT2DEVINFO && 39720c794b3Sgavinm infocmd != DDI_INFO_DEVT2INSTANCE) || inb_dip == NULL) { 39820c794b3Sgavinm *result = NULL; 39920c794b3Sgavinm return (DDI_FAILURE); 40020c794b3Sgavinm } 40120c794b3Sgavinm if (infocmd == DDI_INFO_DEVT2DEVINFO) 40220c794b3Sgavinm *result = inb_dip; 40320c794b3Sgavinm else 40420c794b3Sgavinm *result = (void *)(uintptr_t)ddi_get_instance(inb_dip); 40520c794b3Sgavinm return (0); 40620c794b3Sgavinm } 40720c794b3Sgavinm 40820c794b3Sgavinm static int 40920c794b3Sgavinm inb_mc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 41020c794b3Sgavinm { 41120c794b3Sgavinm if (cmd == DDI_RESUME) { 41220c794b3Sgavinm nb_dev_reinit(); 41320c794b3Sgavinm return (DDI_SUCCESS); 41420c794b3Sgavinm } 41520c794b3Sgavinm if (cmd != DDI_ATTACH) 41620c794b3Sgavinm return (DDI_FAILURE); 41720c794b3Sgavinm if (inb_dip == NULL) { 41820c794b3Sgavinm inb_dip = dip; 41930cfc677SAdrian Frost nb_no_smbios = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 42030cfc677SAdrian Frost DDI_PROP_DONTPASS, "no-smbios", 0); 42120c794b3Sgavinm nb_pci_cfg_setup(dip); 4229ff4cbe7SAdrian Frost (void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model", 4239ff4cbe7SAdrian Frost inb_mc_name()); 42420c794b3Sgavinm if (nb_dev_init()) { 42520c794b3Sgavinm nb_pci_cfg_free(); 42620c794b3Sgavinm inb_dip = NULL; 42720c794b3Sgavinm return (DDI_FAILURE); 42820c794b3Sgavinm } 42920c794b3Sgavinm if (ddi_create_minor_node(dip, "mc-intel", S_IFCHR, 0, 43020c794b3Sgavinm "ddi_mem_ctrl", 0) != DDI_SUCCESS) { 43120c794b3Sgavinm cmn_err(CE_WARN, "failed to create minor node" 43220c794b3Sgavinm " for memory controller\n"); 43320c794b3Sgavinm } 43420c794b3Sgavinm cmi_hdl_walk(inb_mc_register, NULL, NULL, NULL); 43520c794b3Sgavinm } 43620c794b3Sgavinm 43720c794b3Sgavinm return (DDI_SUCCESS); 43820c794b3Sgavinm } 43920c794b3Sgavinm 44020c794b3Sgavinm /*ARGSUSED*/ 44120c794b3Sgavinm static int 44220c794b3Sgavinm inb_mc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 44320c794b3Sgavinm { 44420c794b3Sgavinm if (nb_allow_detach && cmd == DDI_DETACH && dip == inb_dip) { 44520c794b3Sgavinm rw_enter(&inb_mc_lock, RW_WRITER); 44620c794b3Sgavinm inb_mc_snapshot_destroy(); 44720c794b3Sgavinm rw_exit(&inb_mc_lock); 44820c794b3Sgavinm inb_dip = NULL; 44920c794b3Sgavinm return (DDI_SUCCESS); 45020c794b3Sgavinm } else if (cmd == DDI_SUSPEND || cmd == DDI_PM_SUSPEND) { 45120c794b3Sgavinm return (DDI_SUCCESS); 45220c794b3Sgavinm } else { 45320c794b3Sgavinm return (DDI_FAILURE); 45420c794b3Sgavinm } 45520c794b3Sgavinm } 45620c794b3Sgavinm 45720c794b3Sgavinm /*ARGSUSED*/ 45820c794b3Sgavinm static int 45920c794b3Sgavinm inb_mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 46020c794b3Sgavinm { 46120c794b3Sgavinm if (otyp != OTYP_CHR) 46220c794b3Sgavinm return (EINVAL); 46320c794b3Sgavinm 46420c794b3Sgavinm rw_enter(&inb_mc_lock, RW_READER); 46520c794b3Sgavinm if (getminor(*devp) >= 1) { 46620c794b3Sgavinm rw_exit(&inb_mc_lock); 46720c794b3Sgavinm return (EINVAL); 46820c794b3Sgavinm } 46920c794b3Sgavinm rw_exit(&inb_mc_lock); 47020c794b3Sgavinm 47120c794b3Sgavinm return (0); 47220c794b3Sgavinm } 47320c794b3Sgavinm 47420c794b3Sgavinm /*ARGSUSED*/ 47520c794b3Sgavinm static int 47620c794b3Sgavinm inb_mc_close(dev_t dev, int flag, int otyp, cred_t *credp) 47720c794b3Sgavinm { 47820c794b3Sgavinm return (0); 47920c794b3Sgavinm } 48020c794b3Sgavinm 48120c794b3Sgavinm 48220c794b3Sgavinm static struct cb_ops inb_mc_cb_ops = { 48320c794b3Sgavinm inb_mc_open, 48420c794b3Sgavinm inb_mc_close, 48520c794b3Sgavinm nodev, /* not a block driver */ 48620c794b3Sgavinm nodev, /* no print routine */ 48720c794b3Sgavinm nodev, /* no dump routine */ 48820c794b3Sgavinm nodev, /* no read routine */ 48920c794b3Sgavinm nodev, /* no write routine */ 49020c794b3Sgavinm inb_mc_ioctl, 49120c794b3Sgavinm nodev, /* no devmap routine */ 49220c794b3Sgavinm nodev, /* no mmap routine */ 49320c794b3Sgavinm nodev, /* no segmap routine */ 49420c794b3Sgavinm nochpoll, /* no chpoll routine */ 49520c794b3Sgavinm ddi_prop_op, 49620c794b3Sgavinm 0, /* not a STREAMS driver */ 49720c794b3Sgavinm D_NEW | D_MP, /* safe for multi-thread/multi-processor */ 49820c794b3Sgavinm }; 49920c794b3Sgavinm 50020c794b3Sgavinm static struct dev_ops inb_mc_ops = { 50120c794b3Sgavinm DEVO_REV, /* devo_rev */ 50220c794b3Sgavinm 0, /* devo_refcnt */ 50320c794b3Sgavinm inb_mc_getinfo, /* devo_getinfo */ 50420c794b3Sgavinm nulldev, /* devo_identify */ 50520c794b3Sgavinm nulldev, /* devo_probe */ 50620c794b3Sgavinm inb_mc_attach, /* devo_attach */ 50720c794b3Sgavinm inb_mc_detach, /* devo_detach */ 50820c794b3Sgavinm nodev, /* devo_reset */ 50920c794b3Sgavinm &inb_mc_cb_ops, /* devo_cb_ops */ 51020c794b3Sgavinm NULL, /* devo_bus_ops */ 51119397407SSherry Moore NULL, /* devo_power */ 51219397407SSherry Moore ddi_quiesce_not_needed, /* devo_quiesce */ 51320c794b3Sgavinm }; 51420c794b3Sgavinm 51520c794b3Sgavinm static struct modldrv modldrv = { 51620c794b3Sgavinm &mod_driverops, 51720c794b3Sgavinm "Intel 5000 Memory Controller Hub Module", 51820c794b3Sgavinm &inb_mc_ops 51920c794b3Sgavinm }; 52020c794b3Sgavinm 52120c794b3Sgavinm static struct modlinkage modlinkage = { 52220c794b3Sgavinm MODREV_1, 52320c794b3Sgavinm (void *)&modldrv, 52420c794b3Sgavinm NULL 52520c794b3Sgavinm }; 52620c794b3Sgavinm 52720c794b3Sgavinm int 52820c794b3Sgavinm _init(void) 52920c794b3Sgavinm { 53020c794b3Sgavinm int err; 53120c794b3Sgavinm 53220c794b3Sgavinm err = nb_init(); 53320c794b3Sgavinm if (err == 0 && (err = mod_install(&modlinkage)) == 0) 53420c794b3Sgavinm rw_init(&inb_mc_lock, NULL, RW_DRIVER, NULL); 53520c794b3Sgavinm 53620c794b3Sgavinm return (err); 53720c794b3Sgavinm } 53820c794b3Sgavinm 53920c794b3Sgavinm int 54020c794b3Sgavinm _info(struct modinfo *modinfop) 54120c794b3Sgavinm { 54220c794b3Sgavinm return (mod_info(&modlinkage, modinfop)); 54320c794b3Sgavinm } 54420c794b3Sgavinm 54520c794b3Sgavinm int 54620c794b3Sgavinm _fini(void) 54720c794b3Sgavinm { 54820c794b3Sgavinm int err; 54920c794b3Sgavinm 55020c794b3Sgavinm if ((err = mod_remove(&modlinkage)) == 0) { 55120c794b3Sgavinm nb_unload(); 55220c794b3Sgavinm rw_destroy(&inb_mc_lock); 55320c794b3Sgavinm } 55420c794b3Sgavinm 55520c794b3Sgavinm return (err); 55620c794b3Sgavinm } 557