11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51708Sstevel * Common Development and Distribution License (the "License").
61708Sstevel * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
21*11311SSurya.Prakki@Sun.COM
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel
271708Sstevel #include <sys/ddi.h>
281708Sstevel #include <sys/plat_ecc_dimm.h>
291708Sstevel
301708Sstevel extern int plat_max_mc_units_per_board(void);
311708Sstevel extern int plat_ecc_dispatch_task(plat_ecc_message_t *);
321708Sstevel
331708Sstevel /* Platform specific function to get DIMM offset information */
341708Sstevel int (*p2get_mem_offset)(uint64_t, uint64_t *);
351708Sstevel
361708Sstevel /* Platform specific function to get dimm serial id information */
371708Sstevel int (*p2get_mem_sid)(int, int, char *, int, int *);
381708Sstevel
391708Sstevel /*
401708Sstevel * Platform specific function to convert a DIMM location/serial id and
411708Sstevel * offset into a physical address.
421708Sstevel */
431708Sstevel int (*p2get_mem_addr)(int, char *, uint64_t, uint64_t *);
441708Sstevel
451708Sstevel /*
461708Sstevel * Timeouts variable for determining when to give up waiting for a
471708Sstevel * response from the SC. The value is in seconds and the default is
481708Sstevel * based on the current default mailbox timeout used for Serengeti
491708Sstevel * mailbox requests which is 30 seconds (Starcat uses a smaller value).
501708Sstevel */
511708Sstevel int plat_dimm_req_timeout = 30;
521708Sstevel int plat_dimm_req_min_timeout = 6;
531708Sstevel
541708Sstevel /* Number of times to retries DIMM serial id requests */
551708Sstevel int plat_dimm_req_max_retries = 1;
561708Sstevel
571708Sstevel static void plat_request_all_mem_sids(uint32_t);
581708Sstevel
591708Sstevel int
plat_get_mem_sid(char * unum,char * buf,int buflen,int * lenp)601708Sstevel plat_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
611708Sstevel {
621708Sstevel int board, pos, bank, dimm, jnumber;
631708Sstevel int mcid;
641708Sstevel
651708Sstevel if (p2get_mem_sid == NULL ||
661708Sstevel (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
671708Sstevel return (ENOTSUP);
681708Sstevel
691708Sstevel if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
701708Sstevel &jnumber) != 0)
711708Sstevel return (EINVAL);
721708Sstevel
731708Sstevel if (dimm < 0)
741708Sstevel return (EINVAL);
751708Sstevel
761708Sstevel mcid = plat_make_fru_cpuid(board, 0, pos);
771708Sstevel dimm += (bank * 4); /* convert dimm from 0-3 to 0-7 value */
781708Sstevel
791708Sstevel return (p2get_mem_sid(mcid, dimm, buf, buflen, lenp));
801708Sstevel }
811708Sstevel
821708Sstevel int
plat_get_mem_offset(uint64_t paddr,uint64_t * offp)831708Sstevel plat_get_mem_offset(uint64_t paddr, uint64_t *offp)
841708Sstevel {
851708Sstevel if (p2get_mem_offset != NULL) {
861708Sstevel return (p2get_mem_offset(paddr, offp));
871708Sstevel } else
881708Sstevel return (ENOTSUP);
891708Sstevel }
901708Sstevel
911708Sstevel int
plat_get_mem_addr(char * unum,char * sid,uint64_t offset,uint64_t * addrp)921708Sstevel plat_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *addrp)
931708Sstevel {
941708Sstevel int board, pos, bank, dimm, jnumber;
951708Sstevel int mcid;
961708Sstevel
971708Sstevel if (p2get_mem_addr == NULL ||
981708Sstevel (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
991708Sstevel return (ENOTSUP);
1001708Sstevel
1011708Sstevel if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
1021708Sstevel &jnumber) != 0)
1031708Sstevel return (EINVAL);
1041708Sstevel
1051708Sstevel mcid = plat_make_fru_cpuid(board, 0, pos);
1061708Sstevel
1071708Sstevel return (p2get_mem_addr(mcid, sid, offset, addrp));
1081708Sstevel }
1091708Sstevel
1101708Sstevel dimm_sid_cache_t *
plat_alloc_sid_cache(int * max_entries)1111708Sstevel plat_alloc_sid_cache(int *max_entries)
1121708Sstevel {
1131708Sstevel dimm_sid_cache_t *cache;
1141708Sstevel int i, bd, p;
1151708Sstevel int max_mc_per_bd = plat_max_mc_units_per_board();
1161708Sstevel
1171708Sstevel *max_entries = plat_max_cpumem_boards() * max_mc_per_bd;
1181708Sstevel
1191708Sstevel cache = (dimm_sid_cache_t *)kmem_zalloc(sizeof (dimm_sid_cache_t) *
1201708Sstevel *max_entries, KM_SLEEP);
1211708Sstevel
1221708Sstevel for (i = 0; i < *max_entries; i++) {
1231708Sstevel bd = i / max_mc_per_bd;
1241708Sstevel p = i % max_mc_per_bd;
1251708Sstevel cache[i].mcid = plat_make_fru_cpuid(bd, 0, p);
1261708Sstevel }
1271708Sstevel
1281708Sstevel return (cache);
1291708Sstevel }
1301708Sstevel
1311708Sstevel static void
plat_populate_sid_cache_one(dimm_sid_cache_t * cache,int bd)1321708Sstevel plat_populate_sid_cache_one(dimm_sid_cache_t *cache, int bd)
1331708Sstevel {
1341708Sstevel int i, j;
1351708Sstevel uint8_t valid;
1361708Sstevel dimm_sid_t *dimmsidsp;
1371708Sstevel int max_mc_per_bd = plat_max_mc_units_per_board();
1381708Sstevel
1391708Sstevel
1401708Sstevel /*
1411708Sstevel * There must be at least one dimm on the board for this
1421708Sstevel * code to be called.
1431708Sstevel */
1441708Sstevel ASSERT(domain_dimm_sids[bd].pdsb_valid_bitmap);
1451708Sstevel
1461708Sstevel for (i = 0; i < max_mc_per_bd; i++) {
1471708Sstevel int index = bd * max_mc_per_bd + i;
1481708Sstevel
1491708Sstevel /*
1501708Sstevel * Each entry in the cache represents one mc.
1511708Sstevel * If state is not MC_DIMM_SIDS_REQUESTED, then that mc
1521708Sstevel * either has no DIMMs, is not present, or already has
1531708Sstevel * DIMM serial ids available from a previous call to this
1541708Sstevel * function.
1551708Sstevel */
1561708Sstevel if (cache[index].state != MC_DIMM_SIDS_REQUESTED)
1571708Sstevel continue;
1581708Sstevel
1591708Sstevel valid = domain_dimm_sids[bd].pdsb_valid_bitmap >> (i * 8) &
1601708Sstevel 0xff;
1611708Sstevel
1621708Sstevel dimmsidsp = cache[index].sids;
1631708Sstevel
1641708Sstevel /*
1651708Sstevel * Copy the valid DIMM serial ids. Each mc can have up to
1661708Sstevel * eight DIMMs.
1671708Sstevel */
1681708Sstevel for (j = 0; j < 8; j++) {
1691708Sstevel if (((1 << j) & valid) == 0)
1701708Sstevel continue;
1711708Sstevel
172*11311SSurya.Prakki@Sun.COM (void) strncpy(dimmsidsp[j],
1731708Sstevel domain_dimm_sids[bd].pdsb_dimm_sids[(i * 8) + j],
1741708Sstevel PLAT_MAX_DIMM_SID_LEN);
1751708Sstevel }
1761708Sstevel
1771708Sstevel cache[index].state = MC_DIMM_SIDS_AVAILABLE;
1781708Sstevel }
1791708Sstevel }
1801708Sstevel
1811708Sstevel int
plat_populate_sid_cache(dimm_sid_cache_t * cache,int max_entries)1821708Sstevel plat_populate_sid_cache(dimm_sid_cache_t *cache, int max_entries)
1831708Sstevel {
1841708Sstevel int i;
1851708Sstevel int bd;
1861708Sstevel uint32_t bds = 0, retry_bds = 0;
1871708Sstevel int max_mc_per_bd = plat_max_mc_units_per_board();
1881708Sstevel clock_t start_lbolt, current_lbolt;
1891708Sstevel ulong_t elapsed_sec;
1901708Sstevel int max_retries = plat_dimm_req_max_retries;
1911708Sstevel
1921708Sstevel for (i = 0; i < max_entries; i++) {
1931708Sstevel if (cache[i].state == MC_DIMM_SIDS_REQUESTED) {
1941708Sstevel bd = i / max_mc_per_bd;
1951708Sstevel bds |= (1 << bd);
1961708Sstevel }
1971708Sstevel }
1981708Sstevel
1991708Sstevel retry:
2001708Sstevel plat_request_all_mem_sids(bds);
2011708Sstevel
2021708Sstevel /*
2031708Sstevel * Wait for mailbox messages from SC.
2041708Sstevel * Keep track of elapsed time in order to avoid getting
2051708Sstevel * stuck here if something is wrong with the SC.
2061708Sstevel */
2071708Sstevel if (plat_dimm_req_timeout < plat_dimm_req_min_timeout) {
2081708Sstevel cmn_err(CE_WARN, "plat_dimm_req_timeout (%d secs) is less "
2091708Sstevel "than the minimum value (%d secs). Resetting to "
2101708Sstevel "minimum.", plat_dimm_req_timeout,
2111708Sstevel plat_dimm_req_min_timeout);
2121708Sstevel plat_dimm_req_timeout = plat_dimm_req_min_timeout;
2131708Sstevel }
2141708Sstevel
2151708Sstevel start_lbolt = ddi_get_lbolt();
2161708Sstevel
2171708Sstevel while (bds) {
2181708Sstevel for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
2191708Sstevel if (((1 << bd) & bds) == 0)
2201708Sstevel continue;
2211708Sstevel
2221708Sstevel switch (domain_dimm_sids[bd].pdsb_state) {
2231708Sstevel case PDSB_STATE_STORE_IN_PROGRESS:
2241708Sstevel /* Check elapsed time for possible timeout. */
2251708Sstevel current_lbolt = ddi_get_lbolt();
2261708Sstevel elapsed_sec = TICK_TO_SEC(current_lbolt -
2271708Sstevel start_lbolt);
2281708Sstevel if (elapsed_sec > plat_dimm_req_timeout) {
2291708Sstevel mutex_enter(&domain_dimm_sids[bd].
2301708Sstevel pdsb_lock);
2311708Sstevel domain_dimm_sids[bd].pdsb_state =
2321708Sstevel PDSB_STATE_FAILED_TO_STORE;
2331708Sstevel mutex_exit(&domain_dimm_sids[bd].
2341708Sstevel pdsb_lock);
2351708Sstevel }
2361708Sstevel continue;
2371708Sstevel
2381708Sstevel case PDSB_STATE_FAILED_TO_STORE:
2391708Sstevel /* Record board# for possible retry */
2401708Sstevel retry_bds |= (1 << bd);
2411708Sstevel break;
2421708Sstevel
2431708Sstevel case PDSB_STATE_STORED:
2441708Sstevel /* Success! */
2451708Sstevel plat_populate_sid_cache_one(cache, bd);
2461708Sstevel break;
2471708Sstevel
2481708Sstevel default:
2491708Sstevel cmn_err(CE_PANIC, "Unknown state (0x%x) for "
2501708Sstevel "domain_dimm_sids[%d]",
2511708Sstevel domain_dimm_sids[bd].pdsb_state, bd);
2521708Sstevel }
2531708Sstevel
2541708Sstevel bds &= ~(1 << bd);
2551708Sstevel }
2561708Sstevel /*
2571708Sstevel * If there are still outstanding requests, delay for one half
2581708Sstevel * second to avoid excessive busy waiting.
2591708Sstevel */
2601708Sstevel if (bds != 0)
2611708Sstevel delay(drv_usectohz(500000));
2621708Sstevel }
2631708Sstevel
2641708Sstevel if (max_retries-- && retry_bds) {
2651708Sstevel bds = retry_bds;
2661708Sstevel retry_bds = 0;
2671708Sstevel goto retry;
2681708Sstevel } else if (!max_retries && retry_bds) {
2691708Sstevel cmn_err(CE_WARN, "!Unable to retrieve DIMM serial ids for "
2701708Sstevel "boards 0x%x", retry_bds);
2711708Sstevel return (ETIMEDOUT);
2721708Sstevel }
2731708Sstevel
2741708Sstevel return (0);
2751708Sstevel }
2761708Sstevel
2771708Sstevel /*
2781708Sstevel * Functions for requesting DIMM serial id information from the SC and
2791708Sstevel * updating and storing it on the domain for use by the Memory Controller
2801708Sstevel * driver.
2811708Sstevel */
2821708Sstevel
2831708Sstevel /*
2841708Sstevel * Adds DIMM serial id data received from the SC to the domain_dimm_sids[]
2851708Sstevel * array. Called by the Serengeti and Starcat mailbox code that handles the
2861708Sstevel * reply message from the SC containing a plat_dimm_sid_board_data_t.
2871708Sstevel */
2881708Sstevel int
plat_store_mem_sids(plat_dimm_sid_board_data_t * data)2891708Sstevel plat_store_mem_sids(plat_dimm_sid_board_data_t *data)
2901708Sstevel {
2911708Sstevel int bd;
2921708Sstevel int i;
2931708Sstevel
2941708Sstevel bd = data->pdsbd_board_num;
2951708Sstevel
2961708Sstevel mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
2971708Sstevel
2981708Sstevel if (data->pdsbd_errno) {
2991708Sstevel domain_dimm_sids[bd].pdsb_state = PDSB_STATE_FAILED_TO_STORE;
3001708Sstevel mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
3011708Sstevel cmn_err(CE_WARN, "!plat_store_mem_sids: bd %d errno %d", bd,
3021708Sstevel data->pdsbd_errno);
3031708Sstevel return (data->pdsbd_errno);
3041708Sstevel }
3051708Sstevel
3061708Sstevel domain_dimm_sids[bd].pdsb_valid_bitmap = data->pdsbd_valid_bitmap;
3071708Sstevel for (i = 0; i < PLAT_MAX_DIMMS_PER_BOARD; i++) {
3081708Sstevel if ((1 << i) & domain_dimm_sids[bd].pdsb_valid_bitmap) {
309*11311SSurya.Prakki@Sun.COM (void) strncpy(domain_dimm_sids[bd].pdsb_dimm_sids[i],
3101708Sstevel data->pdsbd_dimm_sids[i], PLAT_MAX_DIMM_SID_LEN);
3111708Sstevel }
3121708Sstevel }
3131708Sstevel domain_dimm_sids[bd].pdsb_state = PDSB_STATE_STORED;
3141708Sstevel
3151708Sstevel mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
3161708Sstevel
3171708Sstevel return (0);
3181708Sstevel }
3191708Sstevel
3201708Sstevel /*
3211708Sstevel * Calls plat_request_mem_sids(bd) for each board number present in the domain.
3221708Sstevel * Called the first time the capability exchange is successful and the SC
3231708Sstevel * capability indicates support for providing DIMM serial ids.
3241708Sstevel *
3251708Sstevel * The input argument is a bitmask of cpu/mem boards that are present and
3261708Sstevel * have at least one memory controller configured.
3271708Sstevel */
3281708Sstevel static void
plat_request_all_mem_sids(uint32_t bds)3291708Sstevel plat_request_all_mem_sids(uint32_t bds)
3301708Sstevel {
3311708Sstevel int bd;
3321708Sstevel int ret;
3331708Sstevel
3341708Sstevel for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
3351708Sstevel if (!((1 << bd) & bds))
3361708Sstevel continue;
3371708Sstevel
3381708Sstevel ret = plat_request_mem_sids(bd);
3391708Sstevel if (ret) {
3401708Sstevel mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
3411708Sstevel domain_dimm_sids[bd].pdsb_state =
3421708Sstevel PDSB_STATE_FAILED_TO_STORE;
3431708Sstevel mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
3441708Sstevel }
3451708Sstevel }
3461708Sstevel }
3471708Sstevel
3481708Sstevel /*
3491708Sstevel * Initiates a mailbox request to SC for DIMM serial ids for the specified
3501708Sstevel * board number. Called by DR when a CPU/Mem board is connected. Also
3511708Sstevel * called by plat_request_all_mem_sids().
3521708Sstevel */
3531708Sstevel int
plat_request_mem_sids(int boardnum)3541708Sstevel plat_request_mem_sids(int boardnum)
3551708Sstevel {
3561708Sstevel plat_ecc_message_t *wrapperp;
3571708Sstevel plat_dimm_sid_request_data_t *dreqp;
3581708Sstevel
3591708Sstevel if (domain_dimm_sids[boardnum].pdsb_state == PDSB_STATE_STORED)
3601708Sstevel return (0);
3611708Sstevel
3621708Sstevel mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
3631708Sstevel domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_STORE_IN_PROGRESS;
3641708Sstevel mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
3651708Sstevel
3661708Sstevel wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
3671708Sstevel
3681708Sstevel /* Initialize the wrapper */
3691708Sstevel wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
3701708Sstevel wrapperp->ecc_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
3711708Sstevel wrapperp->ecc_msg_len = sizeof (plat_dimm_sid_request_data_t);
3721708Sstevel wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
3731708Sstevel
3741708Sstevel dreqp = (plat_dimm_sid_request_data_t *)wrapperp->ecc_msg_data;
3751708Sstevel
3761708Sstevel /* Fill the header */
3771708Sstevel dreqp->pdsrd_major_version = PLAT_ECC_DIMM_SID_VERSION_MAJOR;
3781708Sstevel dreqp->pdsrd_minor_version = PLAT_ECC_DIMM_SID_VERSION_MINOR;
3791708Sstevel dreqp->pdsrd_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
3801708Sstevel dreqp->pdsrd_msg_length = wrapperp->ecc_msg_len;
3811708Sstevel
3821708Sstevel /* Set board number DIMM serial ids are requested for */
3831708Sstevel dreqp->pdsrd_board_num = boardnum;
3841708Sstevel
3851708Sstevel /*
3861708Sstevel * Send the data on to the queuing function
3871708Sstevel */
3881708Sstevel return (plat_ecc_dispatch_task(wrapperp));
3891708Sstevel }
3901708Sstevel
3911708Sstevel /*
3921708Sstevel * Discards DIMM serial id information from domain_dimm_sids[]
3931708Sstevel * for a particular board.
3941708Sstevel * Called by DR when a CPU/Mem board is disconnected.
3951708Sstevel */
3961708Sstevel int
plat_discard_mem_sids(int boardnum)3971708Sstevel plat_discard_mem_sids(int boardnum)
3981708Sstevel {
3991708Sstevel mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
4001708Sstevel domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_INVALID;
4011708Sstevel mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
4021708Sstevel
4031708Sstevel return (0);
4041708Sstevel }
405