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 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 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 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 * 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 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 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 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 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 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 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