103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 21*07d06da5SSurya Prakki 2203831d35Sstevel /* 23*07d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel #include <sys/ddi.h> 2803831d35Sstevel #include <sys/plat_ecc_dimm.h> 2903831d35Sstevel 3003831d35Sstevel extern int plat_max_mc_units_per_board(void); 3103831d35Sstevel extern int plat_ecc_dispatch_task(plat_ecc_message_t *); 3203831d35Sstevel 3303831d35Sstevel /* Platform specific function to get DIMM offset information */ 3403831d35Sstevel int (*p2get_mem_offset)(uint64_t, uint64_t *); 3503831d35Sstevel 3603831d35Sstevel /* Platform specific function to get dimm serial id information */ 3703831d35Sstevel int (*p2get_mem_sid)(int, int, char *, int, int *); 3803831d35Sstevel 3903831d35Sstevel /* 4003831d35Sstevel * Platform specific function to convert a DIMM location/serial id and 4103831d35Sstevel * offset into a physical address. 4203831d35Sstevel */ 4303831d35Sstevel int (*p2get_mem_addr)(int, char *, uint64_t, uint64_t *); 4403831d35Sstevel 4503831d35Sstevel /* 4603831d35Sstevel * Timeouts variable for determining when to give up waiting for a 4703831d35Sstevel * response from the SC. The value is in seconds and the default is 4803831d35Sstevel * based on the current default mailbox timeout used for Serengeti 4903831d35Sstevel * mailbox requests which is 30 seconds (Starcat uses a smaller value). 5003831d35Sstevel */ 5103831d35Sstevel int plat_dimm_req_timeout = 30; 5203831d35Sstevel int plat_dimm_req_min_timeout = 6; 5303831d35Sstevel 5403831d35Sstevel /* Number of times to retries DIMM serial id requests */ 5503831d35Sstevel int plat_dimm_req_max_retries = 1; 5603831d35Sstevel 5703831d35Sstevel static void plat_request_all_mem_sids(uint32_t); 5803831d35Sstevel 5903831d35Sstevel int 6003831d35Sstevel plat_get_mem_sid(char *unum, char *buf, int buflen, int *lenp) 6103831d35Sstevel { 6203831d35Sstevel int board, pos, bank, dimm, jnumber; 6303831d35Sstevel int mcid; 6403831d35Sstevel 6503831d35Sstevel if (p2get_mem_sid == NULL || 6603831d35Sstevel (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0)) 6703831d35Sstevel return (ENOTSUP); 6803831d35Sstevel 6903831d35Sstevel if (parse_unum_memory(unum, &board, &pos, &bank, &dimm, 7003831d35Sstevel &jnumber) != 0) 7103831d35Sstevel return (EINVAL); 7203831d35Sstevel 7303831d35Sstevel if (dimm < 0) 7403831d35Sstevel return (EINVAL); 7503831d35Sstevel 7603831d35Sstevel mcid = plat_make_fru_cpuid(board, 0, pos); 7703831d35Sstevel dimm += (bank * 4); /* convert dimm from 0-3 to 0-7 value */ 7803831d35Sstevel 7903831d35Sstevel return (p2get_mem_sid(mcid, dimm, buf, buflen, lenp)); 8003831d35Sstevel } 8103831d35Sstevel 8203831d35Sstevel int 8303831d35Sstevel plat_get_mem_offset(uint64_t paddr, uint64_t *offp) 8403831d35Sstevel { 8503831d35Sstevel if (p2get_mem_offset != NULL) { 8603831d35Sstevel return (p2get_mem_offset(paddr, offp)); 8703831d35Sstevel } else 8803831d35Sstevel return (ENOTSUP); 8903831d35Sstevel } 9003831d35Sstevel 9103831d35Sstevel int 9203831d35Sstevel plat_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *addrp) 9303831d35Sstevel { 9403831d35Sstevel int board, pos, bank, dimm, jnumber; 9503831d35Sstevel int mcid; 9603831d35Sstevel 9703831d35Sstevel if (p2get_mem_addr == NULL || 9803831d35Sstevel (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0)) 9903831d35Sstevel return (ENOTSUP); 10003831d35Sstevel 10103831d35Sstevel if (parse_unum_memory(unum, &board, &pos, &bank, &dimm, 10203831d35Sstevel &jnumber) != 0) 10303831d35Sstevel return (EINVAL); 10403831d35Sstevel 10503831d35Sstevel mcid = plat_make_fru_cpuid(board, 0, pos); 10603831d35Sstevel 10703831d35Sstevel return (p2get_mem_addr(mcid, sid, offset, addrp)); 10803831d35Sstevel } 10903831d35Sstevel 11003831d35Sstevel dimm_sid_cache_t * 11103831d35Sstevel plat_alloc_sid_cache(int *max_entries) 11203831d35Sstevel { 11303831d35Sstevel dimm_sid_cache_t *cache; 11403831d35Sstevel int i, bd, p; 11503831d35Sstevel int max_mc_per_bd = plat_max_mc_units_per_board(); 11603831d35Sstevel 11703831d35Sstevel *max_entries = plat_max_cpumem_boards() * max_mc_per_bd; 11803831d35Sstevel 11903831d35Sstevel cache = (dimm_sid_cache_t *)kmem_zalloc(sizeof (dimm_sid_cache_t) * 12003831d35Sstevel *max_entries, KM_SLEEP); 12103831d35Sstevel 12203831d35Sstevel for (i = 0; i < *max_entries; i++) { 12303831d35Sstevel bd = i / max_mc_per_bd; 12403831d35Sstevel p = i % max_mc_per_bd; 12503831d35Sstevel cache[i].mcid = plat_make_fru_cpuid(bd, 0, p); 12603831d35Sstevel } 12703831d35Sstevel 12803831d35Sstevel return (cache); 12903831d35Sstevel } 13003831d35Sstevel 13103831d35Sstevel static void 13203831d35Sstevel plat_populate_sid_cache_one(dimm_sid_cache_t *cache, int bd) 13303831d35Sstevel { 13403831d35Sstevel int i, j; 13503831d35Sstevel uint8_t valid; 13603831d35Sstevel dimm_sid_t *dimmsidsp; 13703831d35Sstevel int max_mc_per_bd = plat_max_mc_units_per_board(); 13803831d35Sstevel 13903831d35Sstevel 14003831d35Sstevel /* 14103831d35Sstevel * There must be at least one dimm on the board for this 14203831d35Sstevel * code to be called. 14303831d35Sstevel */ 14403831d35Sstevel ASSERT(domain_dimm_sids[bd].pdsb_valid_bitmap); 14503831d35Sstevel 14603831d35Sstevel for (i = 0; i < max_mc_per_bd; i++) { 14703831d35Sstevel int index = bd * max_mc_per_bd + i; 14803831d35Sstevel 14903831d35Sstevel /* 15003831d35Sstevel * Each entry in the cache represents one mc. 15103831d35Sstevel * If state is not MC_DIMM_SIDS_REQUESTED, then that mc 15203831d35Sstevel * either has no DIMMs, is not present, or already has 15303831d35Sstevel * DIMM serial ids available from a previous call to this 15403831d35Sstevel * function. 15503831d35Sstevel */ 15603831d35Sstevel if (cache[index].state != MC_DIMM_SIDS_REQUESTED) 15703831d35Sstevel continue; 15803831d35Sstevel 15903831d35Sstevel valid = domain_dimm_sids[bd].pdsb_valid_bitmap >> (i * 8) & 16003831d35Sstevel 0xff; 16103831d35Sstevel 16203831d35Sstevel dimmsidsp = cache[index].sids; 16303831d35Sstevel 16403831d35Sstevel /* 16503831d35Sstevel * Copy the valid DIMM serial ids. Each mc can have up to 16603831d35Sstevel * eight DIMMs. 16703831d35Sstevel */ 16803831d35Sstevel for (j = 0; j < 8; j++) { 16903831d35Sstevel if (((1 << j) & valid) == 0) 17003831d35Sstevel continue; 17103831d35Sstevel 172*07d06da5SSurya Prakki (void) strncpy(dimmsidsp[j], 17303831d35Sstevel domain_dimm_sids[bd].pdsb_dimm_sids[(i * 8) + j], 17403831d35Sstevel PLAT_MAX_DIMM_SID_LEN); 17503831d35Sstevel } 17603831d35Sstevel 17703831d35Sstevel cache[index].state = MC_DIMM_SIDS_AVAILABLE; 17803831d35Sstevel } 17903831d35Sstevel } 18003831d35Sstevel 18103831d35Sstevel int 18203831d35Sstevel plat_populate_sid_cache(dimm_sid_cache_t *cache, int max_entries) 18303831d35Sstevel { 18403831d35Sstevel int i; 18503831d35Sstevel int bd; 18603831d35Sstevel uint32_t bds = 0, retry_bds = 0; 18703831d35Sstevel int max_mc_per_bd = plat_max_mc_units_per_board(); 18803831d35Sstevel clock_t start_lbolt, current_lbolt; 18903831d35Sstevel ulong_t elapsed_sec; 19003831d35Sstevel int max_retries = plat_dimm_req_max_retries; 19103831d35Sstevel 19203831d35Sstevel for (i = 0; i < max_entries; i++) { 19303831d35Sstevel if (cache[i].state == MC_DIMM_SIDS_REQUESTED) { 19403831d35Sstevel bd = i / max_mc_per_bd; 19503831d35Sstevel bds |= (1 << bd); 19603831d35Sstevel } 19703831d35Sstevel } 19803831d35Sstevel 19903831d35Sstevel retry: 20003831d35Sstevel plat_request_all_mem_sids(bds); 20103831d35Sstevel 20203831d35Sstevel /* 20303831d35Sstevel * Wait for mailbox messages from SC. 20403831d35Sstevel * Keep track of elapsed time in order to avoid getting 20503831d35Sstevel * stuck here if something is wrong with the SC. 20603831d35Sstevel */ 20703831d35Sstevel if (plat_dimm_req_timeout < plat_dimm_req_min_timeout) { 20803831d35Sstevel cmn_err(CE_WARN, "plat_dimm_req_timeout (%d secs) is less " 20903831d35Sstevel "than the minimum value (%d secs). Resetting to " 21003831d35Sstevel "minimum.", plat_dimm_req_timeout, 21103831d35Sstevel plat_dimm_req_min_timeout); 21203831d35Sstevel plat_dimm_req_timeout = plat_dimm_req_min_timeout; 21303831d35Sstevel } 21403831d35Sstevel 21503831d35Sstevel start_lbolt = ddi_get_lbolt(); 21603831d35Sstevel 21703831d35Sstevel while (bds) { 21803831d35Sstevel for (bd = 0; bd < plat_max_cpumem_boards(); bd++) { 21903831d35Sstevel if (((1 << bd) & bds) == 0) 22003831d35Sstevel continue; 22103831d35Sstevel 22203831d35Sstevel switch (domain_dimm_sids[bd].pdsb_state) { 22303831d35Sstevel case PDSB_STATE_STORE_IN_PROGRESS: 22403831d35Sstevel /* Check elapsed time for possible timeout. */ 22503831d35Sstevel current_lbolt = ddi_get_lbolt(); 22603831d35Sstevel elapsed_sec = TICK_TO_SEC(current_lbolt - 22703831d35Sstevel start_lbolt); 22803831d35Sstevel if (elapsed_sec > plat_dimm_req_timeout) { 22903831d35Sstevel mutex_enter(&domain_dimm_sids[bd]. 23003831d35Sstevel pdsb_lock); 23103831d35Sstevel domain_dimm_sids[bd].pdsb_state = 23203831d35Sstevel PDSB_STATE_FAILED_TO_STORE; 23303831d35Sstevel mutex_exit(&domain_dimm_sids[bd]. 23403831d35Sstevel pdsb_lock); 23503831d35Sstevel } 23603831d35Sstevel continue; 23703831d35Sstevel 23803831d35Sstevel case PDSB_STATE_FAILED_TO_STORE: 23903831d35Sstevel /* Record board# for possible retry */ 24003831d35Sstevel retry_bds |= (1 << bd); 24103831d35Sstevel break; 24203831d35Sstevel 24303831d35Sstevel case PDSB_STATE_STORED: 24403831d35Sstevel /* Success! */ 24503831d35Sstevel plat_populate_sid_cache_one(cache, bd); 24603831d35Sstevel break; 24703831d35Sstevel 24803831d35Sstevel default: 24903831d35Sstevel cmn_err(CE_PANIC, "Unknown state (0x%x) for " 25003831d35Sstevel "domain_dimm_sids[%d]", 25103831d35Sstevel domain_dimm_sids[bd].pdsb_state, bd); 25203831d35Sstevel } 25303831d35Sstevel 25403831d35Sstevel bds &= ~(1 << bd); 25503831d35Sstevel } 25603831d35Sstevel /* 25703831d35Sstevel * If there are still outstanding requests, delay for one half 25803831d35Sstevel * second to avoid excessive busy waiting. 25903831d35Sstevel */ 26003831d35Sstevel if (bds != 0) 26103831d35Sstevel delay(drv_usectohz(500000)); 26203831d35Sstevel } 26303831d35Sstevel 26403831d35Sstevel if (max_retries-- && retry_bds) { 26503831d35Sstevel bds = retry_bds; 26603831d35Sstevel retry_bds = 0; 26703831d35Sstevel goto retry; 26803831d35Sstevel } else if (!max_retries && retry_bds) { 26903831d35Sstevel cmn_err(CE_WARN, "!Unable to retrieve DIMM serial ids for " 27003831d35Sstevel "boards 0x%x", retry_bds); 27103831d35Sstevel return (ETIMEDOUT); 27203831d35Sstevel } 27303831d35Sstevel 27403831d35Sstevel return (0); 27503831d35Sstevel } 27603831d35Sstevel 27703831d35Sstevel /* 27803831d35Sstevel * Functions for requesting DIMM serial id information from the SC and 27903831d35Sstevel * updating and storing it on the domain for use by the Memory Controller 28003831d35Sstevel * driver. 28103831d35Sstevel */ 28203831d35Sstevel 28303831d35Sstevel /* 28403831d35Sstevel * Adds DIMM serial id data received from the SC to the domain_dimm_sids[] 28503831d35Sstevel * array. Called by the Serengeti and Starcat mailbox code that handles the 28603831d35Sstevel * reply message from the SC containing a plat_dimm_sid_board_data_t. 28703831d35Sstevel */ 28803831d35Sstevel int 28903831d35Sstevel plat_store_mem_sids(plat_dimm_sid_board_data_t *data) 29003831d35Sstevel { 29103831d35Sstevel int bd; 29203831d35Sstevel int i; 29303831d35Sstevel 29403831d35Sstevel bd = data->pdsbd_board_num; 29503831d35Sstevel 29603831d35Sstevel mutex_enter(&domain_dimm_sids[bd].pdsb_lock); 29703831d35Sstevel 29803831d35Sstevel if (data->pdsbd_errno) { 29903831d35Sstevel domain_dimm_sids[bd].pdsb_state = PDSB_STATE_FAILED_TO_STORE; 30003831d35Sstevel mutex_exit(&domain_dimm_sids[bd].pdsb_lock); 30103831d35Sstevel cmn_err(CE_WARN, "!plat_store_mem_sids: bd %d errno %d", bd, 30203831d35Sstevel data->pdsbd_errno); 30303831d35Sstevel return (data->pdsbd_errno); 30403831d35Sstevel } 30503831d35Sstevel 30603831d35Sstevel domain_dimm_sids[bd].pdsb_valid_bitmap = data->pdsbd_valid_bitmap; 30703831d35Sstevel for (i = 0; i < PLAT_MAX_DIMMS_PER_BOARD; i++) { 30803831d35Sstevel if ((1 << i) & domain_dimm_sids[bd].pdsb_valid_bitmap) { 309*07d06da5SSurya Prakki (void) strncpy(domain_dimm_sids[bd].pdsb_dimm_sids[i], 31003831d35Sstevel data->pdsbd_dimm_sids[i], PLAT_MAX_DIMM_SID_LEN); 31103831d35Sstevel } 31203831d35Sstevel } 31303831d35Sstevel domain_dimm_sids[bd].pdsb_state = PDSB_STATE_STORED; 31403831d35Sstevel 31503831d35Sstevel mutex_exit(&domain_dimm_sids[bd].pdsb_lock); 31603831d35Sstevel 31703831d35Sstevel return (0); 31803831d35Sstevel } 31903831d35Sstevel 32003831d35Sstevel /* 32103831d35Sstevel * Calls plat_request_mem_sids(bd) for each board number present in the domain. 32203831d35Sstevel * Called the first time the capability exchange is successful and the SC 32303831d35Sstevel * capability indicates support for providing DIMM serial ids. 32403831d35Sstevel * 32503831d35Sstevel * The input argument is a bitmask of cpu/mem boards that are present and 32603831d35Sstevel * have at least one memory controller configured. 32703831d35Sstevel */ 32803831d35Sstevel static void 32903831d35Sstevel plat_request_all_mem_sids(uint32_t bds) 33003831d35Sstevel { 33103831d35Sstevel int bd; 33203831d35Sstevel int ret; 33303831d35Sstevel 33403831d35Sstevel for (bd = 0; bd < plat_max_cpumem_boards(); bd++) { 33503831d35Sstevel if (!((1 << bd) & bds)) 33603831d35Sstevel continue; 33703831d35Sstevel 33803831d35Sstevel ret = plat_request_mem_sids(bd); 33903831d35Sstevel if (ret) { 34003831d35Sstevel mutex_enter(&domain_dimm_sids[bd].pdsb_lock); 34103831d35Sstevel domain_dimm_sids[bd].pdsb_state = 34203831d35Sstevel PDSB_STATE_FAILED_TO_STORE; 34303831d35Sstevel mutex_exit(&domain_dimm_sids[bd].pdsb_lock); 34403831d35Sstevel } 34503831d35Sstevel } 34603831d35Sstevel } 34703831d35Sstevel 34803831d35Sstevel /* 34903831d35Sstevel * Initiates a mailbox request to SC for DIMM serial ids for the specified 35003831d35Sstevel * board number. Called by DR when a CPU/Mem board is connected. Also 35103831d35Sstevel * called by plat_request_all_mem_sids(). 35203831d35Sstevel */ 35303831d35Sstevel int 35403831d35Sstevel plat_request_mem_sids(int boardnum) 35503831d35Sstevel { 35603831d35Sstevel plat_ecc_message_t *wrapperp; 35703831d35Sstevel plat_dimm_sid_request_data_t *dreqp; 35803831d35Sstevel 35903831d35Sstevel if (domain_dimm_sids[boardnum].pdsb_state == PDSB_STATE_STORED) 36003831d35Sstevel return (0); 36103831d35Sstevel 36203831d35Sstevel mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock); 36303831d35Sstevel domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_STORE_IN_PROGRESS; 36403831d35Sstevel mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock); 36503831d35Sstevel 36603831d35Sstevel wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP); 36703831d35Sstevel 36803831d35Sstevel /* Initialize the wrapper */ 36903831d35Sstevel wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE; 37003831d35Sstevel wrapperp->ecc_msg_type = PLAT_ECC_DIMM_SID_MESSAGE; 37103831d35Sstevel wrapperp->ecc_msg_len = sizeof (plat_dimm_sid_request_data_t); 37203831d35Sstevel wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP); 37303831d35Sstevel 37403831d35Sstevel dreqp = (plat_dimm_sid_request_data_t *)wrapperp->ecc_msg_data; 37503831d35Sstevel 37603831d35Sstevel /* Fill the header */ 37703831d35Sstevel dreqp->pdsrd_major_version = PLAT_ECC_DIMM_SID_VERSION_MAJOR; 37803831d35Sstevel dreqp->pdsrd_minor_version = PLAT_ECC_DIMM_SID_VERSION_MINOR; 37903831d35Sstevel dreqp->pdsrd_msg_type = PLAT_ECC_DIMM_SID_MESSAGE; 38003831d35Sstevel dreqp->pdsrd_msg_length = wrapperp->ecc_msg_len; 38103831d35Sstevel 38203831d35Sstevel /* Set board number DIMM serial ids are requested for */ 38303831d35Sstevel dreqp->pdsrd_board_num = boardnum; 38403831d35Sstevel 38503831d35Sstevel /* 38603831d35Sstevel * Send the data on to the queuing function 38703831d35Sstevel */ 38803831d35Sstevel return (plat_ecc_dispatch_task(wrapperp)); 38903831d35Sstevel } 39003831d35Sstevel 39103831d35Sstevel /* 39203831d35Sstevel * Discards DIMM serial id information from domain_dimm_sids[] 39303831d35Sstevel * for a particular board. 39403831d35Sstevel * Called by DR when a CPU/Mem board is disconnected. 39503831d35Sstevel */ 39603831d35Sstevel int 39703831d35Sstevel plat_discard_mem_sids(int boardnum) 39803831d35Sstevel { 39903831d35Sstevel mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock); 40003831d35Sstevel domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_INVALID; 40103831d35Sstevel mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock); 40203831d35Sstevel 40303831d35Sstevel return (0); 40403831d35Sstevel } 405