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