xref: /illumos-gate/usr/src/uts/sun4u/os/plat_ecc_dimm.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/ddi.h>
29 #include <sys/plat_ecc_dimm.h>
30 
31 extern int plat_max_mc_units_per_board(void);
32 extern int plat_ecc_dispatch_task(plat_ecc_message_t *);
33 
34 /* Platform specific function to get DIMM offset information */
35 int (*p2get_mem_offset)(uint64_t, uint64_t *);
36 
37 /* Platform specific function to get dimm serial id information */
38 int (*p2get_mem_sid)(int, int, char *, int, int *);
39 
40 /*
41  * Platform specific function to convert a DIMM location/serial id and
42  * offset into a physical address.
43  */
44 int (*p2get_mem_addr)(int, char *, uint64_t, uint64_t *);
45 
46 /*
47  * Timeouts variable for determining when to give up waiting for a
48  * response from the SC.  The value is in seconds and the default is
49  * based on the current default mailbox timeout used for Serengeti
50  * mailbox requests which is 30 seconds (Starcat uses a smaller value).
51  */
52 int plat_dimm_req_timeout = 30;
53 int plat_dimm_req_min_timeout = 6;
54 
55 /* Number of times to retries DIMM serial id requests */
56 int plat_dimm_req_max_retries = 1;
57 
58 static void  plat_request_all_mem_sids(uint32_t);
59 
60 int
61 plat_get_mem_sid(char *unum, char *buf, int buflen, int *lenp)
62 {
63 	int	board, pos, bank, dimm, jnumber;
64 	int	mcid;
65 
66 	if (p2get_mem_sid == NULL ||
67 	    (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
68 		return (ENOTSUP);
69 
70 	if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
71 	    &jnumber) != 0)
72 		return (EINVAL);
73 
74 	if (dimm < 0)
75 		return (EINVAL);
76 
77 	mcid = plat_make_fru_cpuid(board, 0, pos);
78 	dimm += (bank * 4);	/* convert dimm from 0-3 to 0-7 value */
79 
80 	return (p2get_mem_sid(mcid, dimm, buf, buflen, lenp));
81 }
82 
83 int
84 plat_get_mem_offset(uint64_t paddr, uint64_t *offp)
85 {
86 	if (p2get_mem_offset != NULL) {
87 		return (p2get_mem_offset(paddr, offp));
88 	} else
89 		return (ENOTSUP);
90 }
91 
92 int
93 plat_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *addrp)
94 {
95 	int	board, pos, bank, dimm, jnumber;
96 	int	mcid;
97 
98 	if (p2get_mem_addr == NULL ||
99 	    (plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE) == 0))
100 		return (ENOTSUP);
101 
102 	if (parse_unum_memory(unum, &board, &pos, &bank, &dimm,
103 	    &jnumber) != 0)
104 		return (EINVAL);
105 
106 	mcid = plat_make_fru_cpuid(board, 0, pos);
107 
108 	return (p2get_mem_addr(mcid, sid, offset, addrp));
109 }
110 
111 dimm_sid_cache_t *
112 plat_alloc_sid_cache(int *max_entries)
113 {
114 	dimm_sid_cache_t *cache;
115 	int i, bd, p;
116 	int max_mc_per_bd = plat_max_mc_units_per_board();
117 
118 	*max_entries = plat_max_cpumem_boards() * max_mc_per_bd;
119 
120 	cache = (dimm_sid_cache_t *)kmem_zalloc(sizeof (dimm_sid_cache_t) *
121 	    *max_entries, KM_SLEEP);
122 
123 	for (i = 0; i < *max_entries; i++) {
124 		bd = i / max_mc_per_bd;
125 		p = i % max_mc_per_bd;
126 		cache[i].mcid = plat_make_fru_cpuid(bd, 0, p);
127 	}
128 
129 	return (cache);
130 }
131 
132 static void
133 plat_populate_sid_cache_one(dimm_sid_cache_t *cache, int bd)
134 {
135 	int		i, j;
136 	uint8_t		valid;
137 	dimm_sid_t	*dimmsidsp;
138 	int		max_mc_per_bd = plat_max_mc_units_per_board();
139 
140 
141 	/*
142 	 * There must be at least one dimm on the board for this
143 	 * code to be called.
144 	 */
145 	ASSERT(domain_dimm_sids[bd].pdsb_valid_bitmap);
146 
147 	for (i = 0; i < max_mc_per_bd; i++) {
148 		int index = bd * max_mc_per_bd + i;
149 
150 		/*
151 		 * Each entry in the cache represents one mc.
152 		 * If state is not MC_DIMM_SIDS_REQUESTED, then that mc
153 		 * either has no DIMMs, is not present, or already has
154 		 * DIMM serial ids available from a previous call to this
155 		 * function.
156 		 */
157 		if (cache[index].state != MC_DIMM_SIDS_REQUESTED)
158 			continue;
159 
160 		valid = domain_dimm_sids[bd].pdsb_valid_bitmap >> (i * 8) &
161 		    0xff;
162 
163 		dimmsidsp = cache[index].sids;
164 
165 		/*
166 		 * Copy the valid DIMM serial ids.  Each mc can have up to
167 		 * eight DIMMs.
168 		 */
169 		for (j = 0; j < 8; j++) {
170 			if (((1 << j) & valid) == 0)
171 				continue;
172 
173 			strncpy(dimmsidsp[j],
174 			    domain_dimm_sids[bd].pdsb_dimm_sids[(i * 8) + j],
175 			    PLAT_MAX_DIMM_SID_LEN);
176 		}
177 
178 		cache[index].state = MC_DIMM_SIDS_AVAILABLE;
179 	}
180 }
181 
182 int
183 plat_populate_sid_cache(dimm_sid_cache_t *cache, int max_entries)
184 {
185 	int		i;
186 	int		bd;
187 	uint32_t	bds = 0, retry_bds = 0;
188 	int		max_mc_per_bd = plat_max_mc_units_per_board();
189 	clock_t		start_lbolt, current_lbolt;
190 	ulong_t		elapsed_sec;
191 	int		max_retries = plat_dimm_req_max_retries;
192 
193 	for (i = 0; i < max_entries; i++) {
194 		if (cache[i].state == MC_DIMM_SIDS_REQUESTED) {
195 			bd = i / max_mc_per_bd;
196 			bds |= (1 << bd);
197 		}
198 	}
199 
200 retry:
201 	plat_request_all_mem_sids(bds);
202 
203 	/*
204 	 * Wait for mailbox messages from SC.
205 	 * Keep track of elapsed time in order to avoid getting
206 	 * stuck here if something is wrong with the SC.
207 	 */
208 	if (plat_dimm_req_timeout < plat_dimm_req_min_timeout) {
209 		cmn_err(CE_WARN, "plat_dimm_req_timeout (%d secs) is less "
210 		    "than the minimum value (%d secs).  Resetting to "
211 		    "minimum.", plat_dimm_req_timeout,
212 		    plat_dimm_req_min_timeout);
213 		plat_dimm_req_timeout = plat_dimm_req_min_timeout;
214 	}
215 
216 	start_lbolt = ddi_get_lbolt();
217 
218 	while (bds) {
219 		for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
220 			if (((1 << bd) & bds) == 0)
221 				continue;
222 
223 			switch (domain_dimm_sids[bd].pdsb_state) {
224 			case PDSB_STATE_STORE_IN_PROGRESS:
225 				/* Check elapsed time for possible timeout. */
226 				current_lbolt = ddi_get_lbolt();
227 				elapsed_sec = TICK_TO_SEC(current_lbolt -
228 				    start_lbolt);
229 				if (elapsed_sec > plat_dimm_req_timeout) {
230 					mutex_enter(&domain_dimm_sids[bd].
231 					    pdsb_lock);
232 					domain_dimm_sids[bd].pdsb_state =
233 					    PDSB_STATE_FAILED_TO_STORE;
234 					mutex_exit(&domain_dimm_sids[bd].
235 					    pdsb_lock);
236 				}
237 				continue;
238 
239 			case PDSB_STATE_FAILED_TO_STORE:
240 				/* Record board# for possible retry */
241 				retry_bds |= (1 << bd);
242 				break;
243 
244 			case PDSB_STATE_STORED:
245 				/* Success! */
246 				plat_populate_sid_cache_one(cache, bd);
247 				break;
248 
249 			default:
250 				cmn_err(CE_PANIC, "Unknown state (0x%x) for "
251 				    "domain_dimm_sids[%d]",
252 				    domain_dimm_sids[bd].pdsb_state, bd);
253 			}
254 
255 			bds &= ~(1 << bd);
256 		}
257 		/*
258 		 * If there are still outstanding requests, delay for one half
259 		 * second to avoid excessive busy waiting.
260 		 */
261 		if (bds != 0)
262 			delay(drv_usectohz(500000));
263 	}
264 
265 	if (max_retries-- && retry_bds) {
266 		bds = retry_bds;
267 		retry_bds = 0;
268 		goto retry;
269 	} else if (!max_retries && retry_bds) {
270 		cmn_err(CE_WARN, "!Unable to retrieve DIMM serial ids for "
271 		    "boards 0x%x", retry_bds);
272 		return (ETIMEDOUT);
273 	}
274 
275 	return (0);
276 }
277 
278 /*
279  * Functions for requesting DIMM serial id information from the SC and
280  * updating and storing it on the domain for use by the Memory Controller
281  * driver.
282  */
283 
284 /*
285  * Adds DIMM serial id data received from the SC to the domain_dimm_sids[]
286  * array. Called by the Serengeti and Starcat mailbox code that handles the
287  * reply message from the SC containing a plat_dimm_sid_board_data_t.
288  */
289 int
290 plat_store_mem_sids(plat_dimm_sid_board_data_t *data)
291 {
292 	int	bd;
293 	int	i;
294 
295 	bd = data->pdsbd_board_num;
296 
297 	mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
298 
299 	if (data->pdsbd_errno) {
300 		domain_dimm_sids[bd].pdsb_state = PDSB_STATE_FAILED_TO_STORE;
301 		mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
302 		cmn_err(CE_WARN, "!plat_store_mem_sids: bd %d  errno %d", bd,
303 		    data->pdsbd_errno);
304 		return (data->pdsbd_errno);
305 	}
306 
307 	domain_dimm_sids[bd].pdsb_valid_bitmap = data->pdsbd_valid_bitmap;
308 	for (i = 0; i < PLAT_MAX_DIMMS_PER_BOARD; i++) {
309 		if ((1 << i) & domain_dimm_sids[bd].pdsb_valid_bitmap) {
310 			strncpy(domain_dimm_sids[bd].pdsb_dimm_sids[i],
311 			    data->pdsbd_dimm_sids[i], PLAT_MAX_DIMM_SID_LEN);
312 		}
313 	}
314 	domain_dimm_sids[bd].pdsb_state = PDSB_STATE_STORED;
315 
316 	mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
317 
318 	return (0);
319 }
320 
321 /*
322  * Calls plat_request_mem_sids(bd) for each board number present in the domain.
323  * Called the first time the capability exchange is successful and the SC
324  * capability indicates support for providing DIMM serial ids.
325  *
326  * The input argument is a bitmask of cpu/mem boards that are present and
327  * have at least one memory controller configured.
328  */
329 static void
330 plat_request_all_mem_sids(uint32_t bds)
331 {
332 	int	bd;
333 	int	ret;
334 
335 	for (bd = 0; bd < plat_max_cpumem_boards(); bd++) {
336 		if (!((1 << bd) & bds))
337 			continue;
338 
339 		ret = plat_request_mem_sids(bd);
340 		if (ret) {
341 			mutex_enter(&domain_dimm_sids[bd].pdsb_lock);
342 			domain_dimm_sids[bd].pdsb_state =
343 			    PDSB_STATE_FAILED_TO_STORE;
344 			mutex_exit(&domain_dimm_sids[bd].pdsb_lock);
345 		}
346 	}
347 }
348 
349 /*
350  * Initiates a mailbox request to SC for DIMM serial ids for the specified
351  * board number.  Called by DR when a CPU/Mem board is connected.  Also
352  * called by plat_request_all_mem_sids().
353  */
354 int
355 plat_request_mem_sids(int boardnum)
356 {
357 	plat_ecc_message_t		*wrapperp;
358 	plat_dimm_sid_request_data_t	*dreqp;
359 
360 	if (domain_dimm_sids[boardnum].pdsb_state == PDSB_STATE_STORED)
361 		return (0);
362 
363 	mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
364 	domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_STORE_IN_PROGRESS;
365 	mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
366 
367 	wrapperp = kmem_zalloc(sizeof (plat_ecc_message_t), KM_SLEEP);
368 
369 	/* Initialize the wrapper */
370 	wrapperp->ecc_msg_status = PLAT_ECC_NO_MSG_ACTIVE;
371 	wrapperp->ecc_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
372 	wrapperp->ecc_msg_len = sizeof (plat_dimm_sid_request_data_t);
373 	wrapperp->ecc_msg_data = kmem_zalloc(wrapperp->ecc_msg_len, KM_SLEEP);
374 
375 	dreqp = (plat_dimm_sid_request_data_t *)wrapperp->ecc_msg_data;
376 
377 	/* Fill the header */
378 	dreqp->pdsrd_major_version = PLAT_ECC_DIMM_SID_VERSION_MAJOR;
379 	dreqp->pdsrd_minor_version = PLAT_ECC_DIMM_SID_VERSION_MINOR;
380 	dreqp->pdsrd_msg_type = PLAT_ECC_DIMM_SID_MESSAGE;
381 	dreqp->pdsrd_msg_length = wrapperp->ecc_msg_len;
382 
383 	/* Set board number DIMM serial ids are requested for */
384 	dreqp->pdsrd_board_num = boardnum;
385 
386 	/*
387 	 * Send the data on to the queuing function
388 	 */
389 	return (plat_ecc_dispatch_task(wrapperp));
390 }
391 
392 /*
393  * Discards DIMM serial id information from domain_dimm_sids[]
394  * for a particular board.
395  * Called by DR when a CPU/Mem board is disconnected.
396  */
397 int
398 plat_discard_mem_sids(int boardnum)
399 {
400 	mutex_enter(&domain_dimm_sids[boardnum].pdsb_lock);
401 	domain_dimm_sids[boardnum].pdsb_state = PDSB_STATE_INVALID;
402 	mutex_exit(&domain_dimm_sids[boardnum].pdsb_lock);
403 
404 	return (0);
405 }
406