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/ddi.h>
28 #include <sys/plat_ecc_dimm.h>
29
30 extern int plat_max_mc_units_per_board(void);
31 extern int plat_ecc_dispatch_task(plat_ecc_message_t *);
32
33 /* Platform specific function to get DIMM offset information */
34 int (*p2get_mem_offset)(uint64_t, uint64_t *);
35
36 /* Platform specific function to get dimm serial id information */
37 int (*p2get_mem_sid)(int, int, char *, int, int *);
38
39 /*
40 * Platform specific function to convert a DIMM location/serial id and
41 * offset into a physical address.
42 */
43 int (*p2get_mem_addr)(int, char *, uint64_t, uint64_t *);
44
45 /*
46 * Timeouts variable for determining when to give up waiting for a
47 * response from the SC. The value is in seconds and the default is
48 * based on the current default mailbox timeout used for Serengeti
49 * mailbox requests which is 30 seconds (Starcat uses a smaller value).
50 */
51 int plat_dimm_req_timeout = 30;
52 int plat_dimm_req_min_timeout = 6;
53
54 /* Number of times to retries DIMM serial id requests */
55 int plat_dimm_req_max_retries = 1;
56
57 static void plat_request_all_mem_sids(uint32_t);
58
59 int
plat_get_mem_sid(char * unum,char * buf,int buflen,int * lenp)60 plat_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
61 {
62 int board, pos, bank, dimm, jnumber;
63 int mcid;
64
65 if (p2get_mem_sid == NULL ||
66 (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
67 return (ENOTSUP);
68
69 if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
70 &jnumber) != 0)
71 return (EINVAL);
72
73 if (dimm < 0)
74 return (EINVAL);
75
76 mcid = plat_make_fru_cpuid(board, 0, pos);
77 dimm += (bank * 4); /* convert dimm from 0-3 to 0-7 value */
78
79 return (p2get_mem_sid(mcid, dimm, buf, buflen, lenp));
80 }
81
82 int
plat_get_mem_offset(uint64_t paddr,uint64_t * offp)83 plat_get_mem_offset(uint64_t paddr, uint64_t *offp)
84 {
85 if (p2get_mem_offset != NULL) {
86 return (p2get_mem_offset(paddr, offp));
87 } else
88 return (ENOTSUP);
89 }
90
91 int
plat_get_mem_addr(char * unum,char * sid,uint64_t offset,uint64_t * addrp)92 plat_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *addrp)
93 {
94 int board, pos, bank, dimm, jnumber;
95 int mcid;
96
97 if (p2get_mem_addr == NULL ||
98 (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
99 return (ENOTSUP);
100
101 if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
102 &jnumber) != 0)
103 return (EINVAL);
104
105 mcid = plat_make_fru_cpuid(board, 0, pos);
106
107 return (p2get_mem_addr(mcid, sid, offset, addrp));
108 }
109
110 dimm_sid_cache_t *
plat_alloc_sid_cache(int * max_entries)111 plat_alloc_sid_cache(int *max_entries)
112 {
113 dimm_sid_cache_t *cache;
114 int i, bd, p;
115 int max_mc_per_bd = plat_max_mc_units_per_board();
116
117 *max_entries = plat_max_cpumem_boards() * max_mc_per_bd;
118
119 cache = (dimm_sid_cache_t *)kmem_zalloc(sizeof (dimm_sid_cache_t) *
120 *max_entries, KM_SLEEP);
121
122 for (i = 0; i < *max_entries; i++) {
123 bd = i / max_mc_per_bd;
124 p = i % max_mc_per_bd;
125 cache[i].mcid = plat_make_fru_cpuid(bd, 0, p);
126 }
127
128 return (cache);
129 }
130
131 static void
plat_populate_sid_cache_one(dimm_sid_cache_t * cache,int bd)132 plat_populate_sid_cache_one(dimm_sid_cache_t *cache, int bd)
133 {
134 int i, j;
135 uint8_t valid;
136 dimm_sid_t *dimmsidsp;
137 int max_mc_per_bd = plat_max_mc_units_per_board();
138
139
140 /*
141 * There must be at least one dimm on the board for this
142 * code to be called.
143 */
144 ASSERT(domain_dimm_sids[bd].pdsb_valid_bitmap);
145
146 for (i = 0; i < max_mc_per_bd; i++) {
147 int index = bd * max_mc_per_bd + i;
148
149 /*
150 * Each entry in the cache represents one mc.
151 * If state is not MC_DIMM_SIDS_REQUESTED, then that mc
152 * either has no DIMMs, is not present, or already has
153 * DIMM serial ids available from a previous call to this
154 * function.
155 */
156 if (cache[index].state != MC_DIMM_SIDS_REQUESTED)
157 continue;
158
159 valid = domain_dimm_sids[bd].pdsb_valid_bitmap >> (i * 8) &
160 0xff;
161
162 dimmsidsp = cache[index].sids;
163
164 /*
165 * Copy the valid DIMM serial ids. Each mc can have up to
166 * eight DIMMs.
167 */
168 for (j = 0; j < 8; j++) {
169 if (((1 << j) & valid) == 0)
170 continue;
171
172 (void) strncpy(dimmsidsp[j],
173 domain_dimm_sids[bd].pdsb_dimm_sids[(i * 8) + j],
174 PLAT_MAX_DIMM_SID_LEN);
175 }
176
177 cache[index].state = MC_DIMM_SIDS_AVAILABLE;
178 }
179 }
180
181 int
plat_populate_sid_cache(dimm_sid_cache_t * cache,int max_entries)182 plat_populate_sid_cache(dimm_sid_cache_t *cache, int max_entries)
183 {
184 int i;
185 int bd;
186 uint32_t bds = 0, retry_bds = 0;
187 int max_mc_per_bd = plat_max_mc_units_per_board();
188 clock_t start_lbolt, current_lbolt;
189 ulong_t elapsed_sec;
190 int max_retries = plat_dimm_req_max_retries;
191
192 for (i = 0; i < max_entries; i++) {
193 if (cache[i].state == MC_DIMM_SIDS_REQUESTED) {
194 bd = i / max_mc_per_bd;
195 bds |= (1 << bd);
196 }
197 }
198
199 retry:
200 plat_request_all_mem_sids(bds);
201
202 /*
203 * Wait for mailbox messages from SC.
204 * Keep track of elapsed time in order to avoid getting
205 * stuck here if something is wrong with the SC.
206 */
207 if (plat_dimm_req_timeout < plat_dimm_req_min_timeout) {
208 cmn_err(CE_WARN, "plat_dimm_req_timeout (%d secs) is less "
209 "than the minimum value (%d secs). Resetting to "
210 "minimum.", plat_dimm_req_timeout,
211 plat_dimm_req_min_timeout);
212 plat_dimm_req_timeout = plat_dimm_req_min_timeout;
213 }
214
215 start_lbolt = ddi_get_lbolt();
216
217 while (bds) {
218 for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
219 if (((1 << bd) & bds) == 0)
220 continue;
221
222 switch (domain_dimm_sids[bd].pdsb_state) {
223 case PDSB_STATE_STORE_IN_PROGRESS:
224 /* Check elapsed time for possible timeout. */
225 current_lbolt = ddi_get_lbolt();
226 elapsed_sec = TICK_TO_SEC(current_lbolt -
227 start_lbolt);
228 if (elapsed_sec > plat_dimm_req_timeout) {
229 mutex_enter(&domain_dimm_sids[bd].
230 pdsb_lock);
231 domain_dimm_sids[bd].pdsb_state =
232 PDSB_STATE_FAILED_TO_STORE;
233 mutex_exit(&domain_dimm_sids[bd].
234 pdsb_lock);
235 }
236 continue;
237
238 case PDSB_STATE_FAILED_TO_STORE:
239 /* Record board# for possible retry */
240 retry_bds |= (1 << bd);
241 break;
242
243 case PDSB_STATE_STORED:
244 /* Success! */
245 plat_populate_sid_cache_one(cache, bd);
246 break;
247
248 default:
249 cmn_err(CE_PANIC, "Unknown state (0x%x) for "
250 "domain_dimm_sids[%d]",
251 domain_dimm_sids[bd].pdsb_state, bd);
252 }
253
254 bds &= ~(1 << bd);
255 }
256 /*
257 * If there are still outstanding requests, delay for one half
258 * second to avoid excessive busy waiting.
259 */
260 if (bds != 0)
261 delay(drv_usectohz(500000));
262 }
263
264 if (max_retries-- && retry_bds) {
265 bds = retry_bds;
266 retry_bds = 0;
267 goto retry;
268 } else if (!max_retries && retry_bds) {
269 cmn_err(CE_WARN, "!Unable to retrieve DIMM serial ids for "
270 "boards 0x%x", retry_bds);
271 return (ETIMEDOUT);
272 }
273
274 return (0);
275 }
276
277 /*
278 * Functions for requesting DIMM serial id information from the SC and
279 * updating and storing it on the domain for use by the Memory Controller
280 * driver.
281 */
282
283 /*
284 * Adds DIMM serial id data received from the SC to the domain_dimm_sids[]
285 * array. Called by the Serengeti and Starcat mailbox code that handles the
286 * reply message from the SC containing a plat_dimm_sid_board_data_t.
287 */
288 int
plat_store_mem_sids(plat_dimm_sid_board_data_t * data)289 plat_store_mem_sids(plat_dimm_sid_board_data_t *data)
290 {
291 int bd;
292 int i;
293
294 bd = data->pdsbd_board_num;
295
296 mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
297
298 if (data->pdsbd_errno) {
299 domain_dimm_sids[bd].pdsb_state = PDSB_STATE_FAILED_TO_STORE;
300 mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
301 cmn_err(CE_WARN, "!plat_store_mem_sids: bd %d errno %d", bd,
302 data->pdsbd_errno);
303 return (data->pdsbd_errno);
304 }
305
306 domain_dimm_sids[bd].pdsb_valid_bitmap = data->pdsbd_valid_bitmap;
307 for (i = 0; i < PLAT_MAX_DIMMS_PER_BOARD; i++) {
308 if ((1 << i) & domain_dimm_sids[bd].pdsb_valid_bitmap) {
309 (void) strncpy(domain_dimm_sids[bd].pdsb_dimm_sids[i],
310 data->pdsbd_dimm_sids[i], PLAT_MAX_DIMM_SID_LEN);
311 }
312 }
313 domain_dimm_sids[bd].pdsb_state = PDSB_STATE_STORED;
314
315 mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
316
317 return (0);
318 }
319
320 /*
321 * Calls plat_request_mem_sids(bd) for each board number present in the domain.
322 * Called the first time the capability exchange is successful and the SC
323 * capability indicates support for providing DIMM serial ids.
324 *
325 * The input argument is a bitmask of cpu/mem boards that are present and
326 * have at least one memory controller configured.
327 */
328 static void
plat_request_all_mem_sids(uint32_t bds)329 plat_request_all_mem_sids(uint32_t bds)
330 {
331 int bd;
332 int ret;
333
334 for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
335 if (!((1 << bd) & bds))
336 continue;
337
338 ret = plat_request_mem_sids(bd);
339 if (ret) {
340 mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
341 domain_dimm_sids[bd].pdsb_state =
342 PDSB_STATE_FAILED_TO_STORE;
343 mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
344 }
345 }
346 }
347
348 /*
349 * Initiates a mailbox request to SC for DIMM serial ids for the specified
350 * board number. Called by DR when a CPU/Mem board is connected. Also
351 * called by plat_request_all_mem_sids().
352 */
353 int
plat_request_mem_sids(int boardnum)354 plat_request_mem_sids(int boardnum)
355 {
356 plat_ecc_message_t *wrapperp;
357 plat_dimm_sid_request_data_t *dreqp;
358
359 if (domain_dimm_sids[boardnum].pdsb_state == PDSB_STATE_STORED)
360 return (0);
361
362 mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
363 domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_STORE_IN_PROGRESS;
364 mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
365
366 wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
367
368 /* Initialize the wrapper */
369 wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
370 wrapperp->ecc_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
371 wrapperp->ecc_msg_len = sizeof (plat_dimm_sid_request_data_t);
372 wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
373
374 dreqp = (plat_dimm_sid_request_data_t *)wrapperp->ecc_msg_data;
375
376 /* Fill the header */
377 dreqp->pdsrd_major_version = PLAT_ECC_DIMM_SID_VERSION_MAJOR;
378 dreqp->pdsrd_minor_version = PLAT_ECC_DIMM_SID_VERSION_MINOR;
379 dreqp->pdsrd_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
380 dreqp->pdsrd_msg_length = wrapperp->ecc_msg_len;
381
382 /* Set board number DIMM serial ids are requested for */
383 dreqp->pdsrd_board_num = boardnum;
384
385 /*
386 * Send the data on to the queuing function
387 */
388 return (plat_ecc_dispatch_task(wrapperp));
389 }
390
391 /*
392 * Discards DIMM serial id information from domain_dimm_sids[]
393 * for a particular board.
394 * Called by DR when a CPU/Mem board is disconnected.
395 */
396 int
plat_discard_mem_sids(int boardnum)397 plat_discard_mem_sids(int boardnum)
398 {
399 mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
400 domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_INVALID;
401 mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
402
403 return (0);
404 }
405