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
fmri2unum(nvlist_t * nvl,mc_unum_t * unump)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
inb_patounum(void * arg,uint64_t pa,uint8_t valid_hi,uint8_t valid_lo,uint32_t synd,int syndtype,mc_unum_t * unump)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
inb_unumtopa(void * arg,mc_unum_t * unump,nvlist_t * nvl,uint64_t * pap)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
dimm_init()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
dimm_fini()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
dimm_add_rank(int branch,int rank,int branch_interleave,int way,uint64_t base,uint32_t hole,uint32_t hole_size,int interleave,uint64_t limit)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
inb_mc_register(cmi_hdl_t hdl,void * arg1,void * arg2,void * arg3)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