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
plat_get_mem_sid(char * unum,char * buf,int buflen,int * lenp)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
plat_get_mem_offset(uint64_t paddr,uint64_t * offp)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
plat_get_mem_addr(char * unum,char * sid,uint64_t offset,uint64_t * addrp)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 *
plat_alloc_sid_cache(int * max_entries)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
plat_populate_sid_cache_one(dimm_sid_cache_t * cache,int bd)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
plat_populate_sid_cache(dimm_sid_cache_t * cache,int max_entries)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
plat_store_mem_sids(plat_dimm_sid_board_data_t * data)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
plat_request_all_mem_sids(uint32_t bds)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
plat_request_mem_sids(int boardnum)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
plat_discard_mem_sids(int boardnum)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