1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/kmem.h> 29 #include <sys/mc.h> 30 #include <sys/nvpair.h> 31 #include <sys/fm/protocol.h> 32 #include <sys/cmn_err.h> 33 #include <sys/sunddi.h> 34 #include <sys/mc_intel.h> 35 #include "nb_log.h" 36 #include "rank.h" 37 #include "dimm_phys.h" 38 #include "nb5000.h" 39 40 struct rank_base *rank_base; 41 42 static int 43 fmri2unum(nvlist_t *nvl, mc_unum_t *unump) 44 { 45 int i; 46 uint64_t offset; 47 nvlist_t **hcl, *hcsp; 48 uint_t npr; 49 50 if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) != 0 || 51 (nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET, 52 &offset) != 0 && nvlist_lookup_uint64(hcsp, 53 FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0) || 54 nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &npr) != 0) 55 return (0); 56 57 58 bzero(unump, sizeof (mc_unum_t)); 59 for (i = 0; i < MC_UNUM_NDIMM; i++) 60 unump->unum_dimms[i] = MC_INVALNUM; 61 62 for (i = 0; i < npr; i++) { 63 char *hcnm, *hcid; 64 long v; 65 66 if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 || 67 nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 || 68 ddi_strtol(hcid, NULL, 0, &v) != 0) 69 return (0); 70 71 if (strcmp(hcnm, "motherboard") == 0) 72 unump->unum_board = (int)v; 73 else if (strcmp(hcnm, "memory-controller") == 0) 74 unump->unum_mc = (int)v; 75 else if (strcmp(hcnm, "dram-channel") == 0) 76 unump->unum_cs = (int)v; 77 else if (strcmp(hcnm, "dimm") == 0) 78 unump->unum_dimms[0] = (int)v; 79 else if (strcmp(hcnm, "rank") == 0) 80 unump->unum_rank = (int)v; 81 } 82 83 return (1); 84 } 85 86 /*ARGSUSED*/ 87 static cmi_errno_t 88 inb_patounum(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo, 89 uint32_t synd, int syndtype, mc_unum_t *unump) 90 { 91 struct rank_base *rp; 92 int i; 93 int last; 94 uint64_t offset; 95 cmi_errno_t rt = CMIERR_UNKNOWN; 96 97 last = nb_dimms_per_channel * nb_number_memory_controllers; 98 for (i = 0; i < last; i++) { 99 rp = &rank_base[i]; 100 if (rp && pa >= rp->base && pa < rp->limit) 101 break; 102 } 103 if (i < last) { 104 offset = pa - rp->base; 105 if (offset > rp->hole) 106 offset -= rp->hole_size; 107 unump->unum_offset = offset / rp->interleave; 108 unump->unum_mc = i / nb_dimms_per_channel; 109 unump->unum_cs = 0; 110 unump->unum_rank = i % nb_dimms_per_channel; 111 rt = CMI_SUCCESS; 112 } 113 return (rt); 114 } 115 116 /*ARGSUSED*/ 117 static cmi_errno_t 118 inb_unumtopa(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap) 119 { 120 int num_ranks_per_branch; 121 mc_unum_t unum; 122 uint64_t pa; 123 struct rank_base *rp; 124 125 if (unump == NULL) { 126 if (!fmri2unum(nvl, &unum)) 127 return (CMI_SUCCESS); 128 unump = &unum; 129 } 130 if ((unump->unum_offset & OFFSET_ROW_BANK_COL)) { 131 if (&dimm_getphys) { 132 pa = dimm_getphys(unump->unum_mc, 133 TCODE_OFFSET_RANK(unump->unum_offset), 134 TCODE_OFFSET_BANK(unump->unum_offset), 135 TCODE_OFFSET_RAS(unump->unum_offset), 136 TCODE_OFFSET_CAS(unump->unum_offset)); 137 if (pa >= MAXPHYS_ADDR) 138 return (CMIERR_MC_NOADDR); 139 } else { 140 return (CMIERR_MC_NOADDR); 141 } 142 *pap = pa; 143 return (CMI_SUCCESS); 144 } 145 146 147 /* max number of ranks per branch */ 148 num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ? 149 NB_5100_RANKS_PER_CHANNEL : 150 nb_dimms_per_channel * nb_channels_per_branch; 151 rp = &rank_base[(unump->unum_mc * num_ranks_per_branch) + 152 unump->unum_rank]; 153 pa = rp->base + (unump->unum_offset * rp->interleave); 154 155 if (rp->hole && pa >= rp->hole) 156 pa += rp->hole_size; 157 *pap = pa; 158 return (CMI_SUCCESS); 159 } 160 161 void 162 dimm_init() 163 { 164 int num_ranks_per_branch; 165 166 167 /* max number of ranks per branch */ 168 num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ? 169 NB_5100_RANKS_PER_CHANNEL : 170 nb_dimms_per_channel * nb_channels_per_branch; 171 172 rank_base = kmem_zalloc(sizeof (struct rank_base) * 173 nb_number_memory_controllers * num_ranks_per_branch, KM_SLEEP); 174 } 175 176 void 177 dimm_fini() 178 { 179 int num_ranks_per_branch; 180 181 182 /* max number of ranks per branch */ 183 num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ? 184 NB_5100_RANKS_PER_CHANNEL : 185 nb_dimms_per_channel * nb_channels_per_branch; 186 187 kmem_free(rank_base, sizeof (struct rank_base) * 188 nb_number_memory_controllers * num_ranks_per_branch); 189 rank_base = 0; 190 } 191 192 void 193 dimm_add_rank(int branch, int rank, int branch_interleave, int way, 194 uint64_t base, uint32_t hole, uint32_t hole_size, int interleave, 195 uint64_t limit) 196 { 197 struct rank_base *rp; 198 int num_ranks_per_branch; 199 200 /* max number of ranks per branch */ 201 num_ranks_per_branch = (nb_chipset == INTEL_NB_5100) ? 202 NB_5100_RANKS_PER_CHANNEL : 203 nb_dimms_per_channel * nb_channels_per_branch; 204 rp = &rank_base[(branch * num_ranks_per_branch) + rank]; 205 rp->branch_interleave = branch_interleave; 206 rp->way = way; 207 rp->base = base; 208 rp->hole = hole; 209 rp->hole_size = hole_size; 210 rp->interleave = interleave; 211 rp->limit = limit; 212 } 213 214 static const cmi_mc_ops_t inb_mc_ops = { 215 inb_patounum, 216 inb_unumtopa, 217 nb_error_trap /* cmi_mc_logout */ 218 }; 219 220 /*ARGSUSED*/ 221 int 222 inb_mc_register(cmi_hdl_t hdl, void *arg1, void *arg2, void *arg3) 223 { 224 cmi_mc_register(hdl, &inb_mc_ops, NULL); 225 return (CMI_HDL_WALK_NEXT); 226 } 227