xref: /titanic_52/usr/src/uts/sun4v/os/fillsysinfo.c (revision ace1a5f11236a072fca1b5e0ea1416a083a9f2aa)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/errno.h>
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/cpu.h>
33 #include <sys/cpuvar.h>
34 #include <sys/clock.h>
35 #include <sys/promif.h>
36 #include <sys/promimpl.h>
37 #include <sys/systm.h>
38 #include <sys/machsystm.h>
39 #include <sys/debug.h>
40 #include <sys/sunddi.h>
41 #include <sys/modctl.h>
42 #include <sys/cpu_module.h>
43 #include <sys/kobj.h>
44 #include <sys/cmp.h>
45 #include <sys/async.h>
46 #include <vm/page.h>
47 
48 /*
49  * The OpenBoot Standalone Interface supplies the kernel with
50  * implementation dependent parameters through the devinfo/property mechanism
51  */
52 typedef enum { XDRBOOL, XDRINT, XDRSTRING } xdrs;
53 
54 /*
55  * structure describing properties that we are interested in querying the
56  * OBP for.
57  */
58 struct getprop_info {
59 	char	*name;
60 	xdrs	type;
61 	uint_t	*var;
62 };
63 
64 /*
65  * structure used to convert between a string returned by the OBP & a type
66  * used within the kernel. We prefer to paramaterize rather than type.
67  */
68 struct convert_info {
69 	char	*name;
70 	uint_t	var;
71 	char	*realname;
72 };
73 
74 /*
75  * structure describing nodes that we are interested in querying the OBP for
76  * properties.
77  */
78 struct node_info {
79 	char			*name;
80 	int			size;
81 	struct getprop_info	*prop;
82 	struct getprop_info	*prop_end;
83 	unsigned int		*value;
84 };
85 
86 /*
87  * macro definitions for routines that form the OBP interface
88  */
89 #define	NEXT			prom_nextnode
90 #define	CHILD			prom_childnode
91 #define	GETPROP			prom_getprop
92 #define	GETPROPLEN		prom_getproplen
93 
94 /* 0=quiet; 1=verbose; 2=debug */
95 int	debug_fillsysinfo = 0;
96 #define	VPRINTF if (debug_fillsysinfo) prom_printf
97 
98 int ncpunode;
99 struct cpu_node cpunodes[NCPU];
100 
101 void	fill_cpu(dnode_t);
102 void	plat_fill_mc(dnode_t);
103 #pragma weak plat_fill_mc
104 
105 uint64_t	system_clock_freq;
106 int		niobus = 0;
107 uint_t		niommu_tsbs = 0;
108 
109 /*
110  * Hardware watchdog support.
111  */
112 #define	CHOSEN_EEPROM	"eeprom"
113 static dnode_t 		chosen_eeprom;
114 
115 /*
116  * If this variable is non-zero, cpr should return "not supported" when
117  * it is queried even though it would normally be supported on this platform.
118  */
119 int cpr_supported_override;
120 
121 /*
122  * Some platforms may need to support CPR even in the absence of the
123  * energystar-v* property (Enchilada server, for example).  If this
124  * variable is non-zero, cpr should proceed even in the absence
125  * of the energystar-v* property.
126  */
127 int cpr_platform_enable = 0;
128 
129 /*
130  * Some nodes have functions that need to be called when they're seen.
131  */
132 static void	have_pci(dnode_t);
133 
134 static struct wkdevice {
135 	char *wk_namep;
136 	void (*wk_func)(dnode_t);
137 	caddr_t *wk_vaddrp;
138 	ushort_t wk_flags;
139 #define	V_OPTIONAL	0x0000
140 #define	V_MUSTHAVE	0x0001
141 #define	V_MAPPED	0x0002
142 #define	V_MULTI		0x0003	/* optional, may be more than one */
143 } wkdevice[] = {
144 	{ "pci", have_pci, NULL, V_MULTI },
145 	{ 0, },
146 };
147 
148 static void map_wellknown(dnode_t);
149 
150 void
151 map_wellknown_devices()
152 {
153 	struct wkdevice *wkp;
154 	phandle_t	ieeprom;
155 	dnode_t	root;
156 	uint_t	stick_freq;
157 
158 	/*
159 	 * if there is a chosen eeprom, note it (for have_eeprom())
160 	 */
161 	if (GETPROPLEN(prom_chosennode(), CHOSEN_EEPROM) ==
162 	    sizeof (phandle_t) &&
163 	    GETPROP(prom_chosennode(), CHOSEN_EEPROM, (caddr_t)&ieeprom) != -1)
164 		chosen_eeprom = (dnode_t)prom_decode_int(ieeprom);
165 
166 	root = prom_nextnode((dnode_t)0);
167 	/*
168 	 * Get System clock frequency from root node if it exists.
169 	 */
170 	if (GETPROP(root, "stick-frequency", (caddr_t)&stick_freq) != -1)
171 		system_clock_freq = stick_freq;
172 
173 	map_wellknown(NEXT((dnode_t)0));
174 
175 	/*
176 	 * See if it worked
177 	 */
178 	for (wkp = wkdevice; wkp->wk_namep; ++wkp) {
179 		if (wkp->wk_flags == V_MUSTHAVE) {
180 			cmn_err(CE_PANIC, "map_wellknown_devices: required "
181 			    "device %s not mapped", wkp->wk_namep);
182 		}
183 	}
184 }
185 
186 /*
187  * map_wellknown - map known devices & registers
188  */
189 static void
190 map_wellknown(dnode_t curnode)
191 {
192 	extern int status_okay(int, char *, int);
193 	char tmp_name[MAXSYSNAME];
194 	static void fill_address(dnode_t, char *);
195 	int sok;
196 
197 #ifdef VPRINTF
198 	VPRINTF("map_wellknown(%x)\n", curnode);
199 #endif /* VPRINTF */
200 
201 	for (curnode = CHILD(curnode); curnode; curnode = NEXT(curnode)) {
202 		/*
203 		 * prune subtree if status property indicating not okay
204 		 */
205 		sok = status_okay((int)curnode, (char *)NULL, 0);
206 		if (!sok) {
207 			char devtype_buf[OBP_MAXPROPNAME];
208 			int size;
209 
210 #ifdef VPRINTF
211 			VPRINTF("map_wellknown: !okay status property\n");
212 #endif /* VPRINTF */
213 			/*
214 			 * a status property indicating bad memory will be
215 			 * associated with a node which has a "device_type"
216 			 * property with a value of "memory-controller"
217 			 */
218 			if ((size = GETPROPLEN(curnode,
219 			    OBP_DEVICETYPE)) == -1)
220 				continue;
221 			if (size > OBP_MAXPROPNAME) {
222 				cmn_err(CE_CONT, "node %x '%s' prop too "
223 				    "big\n", curnode, OBP_DEVICETYPE);
224 				continue;
225 			}
226 			if (GETPROP(curnode, OBP_DEVICETYPE,
227 			    devtype_buf) == -1) {
228 				cmn_err(CE_CONT, "node %x '%s' get failed\n",
229 				    curnode, OBP_DEVICETYPE);
230 				continue;
231 			}
232 			if (strcmp(devtype_buf, "memory-controller") != 0)
233 				continue;
234 			/*
235 			 * ...else fall thru and process the node...
236 			 */
237 		}
238 		bzero(tmp_name, MAXSYSNAME);
239 		if (GETPROP(curnode, OBP_NAME, (caddr_t)tmp_name) != -1)
240 			fill_address(curnode, tmp_name);
241 		if (GETPROP(curnode, OBP_DEVICETYPE, tmp_name) != -1 &&
242 		    strcmp(tmp_name, "cpu") == 0) {
243 			fill_cpu(curnode);
244 		}
245 
246 		if (sok && (strcmp(tmp_name, "memory-controller") == 0) &&
247 		    (&plat_fill_mc != NULL))
248 			plat_fill_mc(curnode);
249 		map_wellknown(curnode);
250 	}
251 }
252 
253 static void
254 fill_address(dnode_t curnode, char *namep)
255 {
256 	struct wkdevice *wkp;
257 	int size;
258 	uint32_t vaddr;
259 
260 	for (wkp = wkdevice; wkp->wk_namep; ++wkp) {
261 		if (strcmp(wkp->wk_namep, namep) != 0)
262 			continue;
263 		if (wkp->wk_flags == V_MAPPED)
264 			return;
265 		if (wkp->wk_vaddrp != NULL) {
266 			if ((size = GETPROPLEN(curnode, OBP_ADDRESS)) == -1) {
267 				cmn_err(CE_CONT, "device %s size %d\n",
268 				    namep, size);
269 				continue;
270 			}
271 			if (size != sizeof (vaddr)) {
272 				cmn_err(CE_CONT, "device %s address prop too "
273 				    "big\n", namep);
274 				continue;
275 			}
276 			if (GETPROP(curnode, OBP_ADDRESS,
277 			    (caddr_t)&vaddr) == -1) {
278 				cmn_err(CE_CONT, "device %s not mapped\n",
279 				    namep);
280 				continue;
281 			}
282 
283 			/* make into a native pointer */
284 			*wkp->wk_vaddrp = (caddr_t)(uintptr_t)vaddr;
285 #ifdef VPRINTF
286 			VPRINTF("fill_address: %s mapped to %p\n", namep,
287 			    *wkp->wk_vaddrp);
288 #endif /* VPRINTF */
289 		}
290 		if (wkp->wk_func != NULL)
291 			(*wkp->wk_func)(curnode);
292 		/*
293 		 * If this one is optional and there may be more than
294 		 * one, don't set V_MAPPED, which would cause us to skip it
295 		 * next time around
296 		 */
297 		if (wkp->wk_flags != V_MULTI)
298 			wkp->wk_flags = V_MAPPED;
299 	}
300 }
301 
302 void
303 fill_cpu(dnode_t node)
304 {
305 	struct cpu_node *cpunode;
306 	processorid_t cpuid;
307 	uint_t clk_freq;
308 	char namebuf[OBP_MAXPROPNAME], unum[UNUM_NAMLEN];
309 	char *namebufp;
310 
311 	if (GETPROP(node, "cpuid", (caddr_t)&cpuid) == -1) {
312 		if (GETPROP(node, "reg", (caddr_t)&cpuid) == -1)
313 			cmn_err(CE_PANIC, "reg prop not found in cpu node");
314 		cpuid = PROM_CFGHDL_TO_CPUID(cpuid);
315 	}
316 
317 	if (cpuid < 0 || cpuid >= NCPU) {
318 		cmn_err(CE_CONT, "cpu (dnode %x): out of range cpuid %d - "
319 		    "cpu excluded from configuration\n", node, cpuid);
320 		return;
321 	}
322 
323 	cpunode = &cpunodes[cpuid];
324 	cpunode->cpuid = cpuid;
325 	cpunode->device_id = cpuid;
326 
327 	unum[0] = '\0';
328 	(void) snprintf(cpunode->fru_fmri, sizeof (cpunode->fru_fmri),
329 		"%s%s", CPU_FRU_FMRI, unum);
330 	(void) GETPROP(node, "compatible", namebuf);
331 	namebufp = namebuf;
332 	if (strncmp(namebufp, "SUNW,", 5) == 0)
333 		namebufp += 5;
334 	(void) strcpy(cpunode->name, namebufp);
335 
336 	if (GETPROP(node, "clock-frequency", (caddr_t)&clk_freq) == -1) {
337 		/*
338 		 * If we didn't find it in the CPU node, look in the root node.
339 		 */
340 		dnode_t root = prom_nextnode((dnode_t)0);
341 		if (GETPROP(root, "clock-frequency", (caddr_t)&clk_freq) == -1)
342 			clk_freq = 0;
343 	}
344 	cpunode->clock_freq = clk_freq;
345 
346 	ASSERT(cpunode->clock_freq != 0);
347 	/*
348 	 * Compute scaling factor based on rate of %tick. This is used
349 	 * to convert from ticks derived from %tick to nanoseconds. See
350 	 * comment in sun4u/sys/clock.h for details.
351 	 */
352 	cpunode->tick_nsec_scale = (uint_t)(((uint64_t)NANOSEC <<
353 	    (32 - TICK_NSEC_SHIFT)) / cpunode->clock_freq);
354 
355 
356 	cpunode->nodeid = node;
357 
358 	/*
359 	 * Call cpu module specific code to fill in the cpu properities
360 	 */
361 	cpu_fiximp(cpunode);
362 }
363 
364 #define	IOMMU_PER_SCHIZO	2
365 
366 /*
367  * The first psycho must always programmed up for the system clock and error
368  * handling purposes.
369  */
370 static void
371 have_pci(dnode_t node)
372 {
373 	int size;
374 	uint_t portid;
375 	char compatible[OBP_MAXDRVNAME];
376 
377 	size = GETPROPLEN(node, "portid");
378 	if (size == -1) size = GETPROPLEN(node, "upa-portid");
379 	if (size == -1)
380 		return;
381 	if (size > sizeof (portid))
382 		cmn_err(CE_PANIC, "portid size wrong");
383 
384 	if (GETPROP(node, "portid", (caddr_t)&portid) == -1)
385 		if (GETPROP(node, "upa-portid", (caddr_t)&portid) == -1)
386 			cmn_err(CE_PANIC, "portid not found");
387 
388 	niobus++;
389 
390 
391 	/*
392 	 * Need two physical TSBs for Schizo-compatible nodes,
393 	 * one otherwise.
394 	 */
395 	compatible[0] = '\0';
396 	(void) prom_getprop(node, OBP_COMPATIBLE, compatible);
397 	if (strcmp(compatible, "pci108e,8001") == 0)
398 		niommu_tsbs += IOMMU_PER_SCHIZO;
399 	else
400 		niommu_tsbs++;
401 }
402 
403 
404 int
405 get_cpu_pagesizes(void)
406 {
407 	/*
408 	 * XXXQ Get supported page sizes information from the PD
409 	 * and return a bit mask indicating which page sizes are
410 	 * supported.
411 	 *
412 	 * Return 0 when no information is available.
413 	 */
414 
415 	return (0);			/* XXXQ for now return 0 as no PD */
416 }
417