xref: /onnv-gate/usr/src/uts/sun4u/os/plat_ecc_dimm.c (revision 11311:639e7bc0b42f)
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