xref: /illumos-gate/usr/src/uts/sun4u/os/plat_ecc_dimm.c (revision d8a7fe16f62711cdc5c4267da8b34ff24a6b668c)
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