1a9da330np/***************************************************************************
2a9da330np *
3a9da330np * devinfo_cpu : cpu devices
4a9da330np *
5a9da330np * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
6a9da330np * Use is subject to license terms.
7a9da330np *
8a9da330np * Licensed under the Academic Free License version 2.1
9a9da330np *
10a9da330np **************************************************************************/
11a9da330np
12a9da330np#pragma ident	"%Z%%M%	%I%	%E% SMI"
13a9da330np
14a9da330np#ifdef HAVE_CONFIG_H
15a9da330np#include <config.h>
16a9da330np#endif
17a9da330np
18a9da330np#include <stdio.h>
19a9da330np#include <string.h>
20a9da330np#include <kstat.h>
21a9da330np#include <sys/utsname.h>
22a9da330np#include <libdevinfo.h>
23a9da330np#include <sys/systeminfo.h>
24a9da330np
25a9da330np#include "../osspec.h"
26a9da330np#include "../logger.h"
27a9da330np#include "../hald.h"
28a9da330np#include "../hald_dbus.h"
29a9da330np#include "../device_info.h"
30a9da330np#include "../util.h"
31a9da330np#include "devinfo_cpu.h"
32a9da330np
33a9da330npstatic HalDevice *devinfo_cpu_add(HalDevice *, di_node_t, char *, char *);
34a9da330np
35a9da330npDevinfoDevHandler devinfo_cpu_handler = {
36a9da330np	devinfo_cpu_add,
37a9da330np	NULL,
38a9da330np	NULL,
39a9da330np	NULL,
40a9da330np	NULL,
41a9da330np	NULL
42a9da330np};
43a9da330np
44a9da330npstatic HalDevice *
45a9da330npdevinfo_cpu_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
46a9da330np{
47a9da330np
48a9da330np	HalDevice	*d;
49a9da330np	char		*prom_device_type = NULL;
50a9da330np	int		*int_cpu_id;
51a9da330np	static int	cpu_id = -1;
52a9da330np	uint64_t	clock_mhz;
53a9da330np	di_prom_handle_t phdl;
54a9da330np	kstat_ctl_t	*kc;
55a9da330np	kstat_t		*ksp;
56a9da330np	kstat_named_t	*ksdata;
57a9da330np	dbus_bool_t	is_supp_freqs;
58a9da330np	char		udi[HAL_PATH_MAX];
59a9da330np	char		*driver_name, *s;
60a9da330np	char		cpu_devfs_path[HAL_PATH_MAX];
61a9da330np
62a9da330np	/*
63a9da330np	 * If it is x86, the software device tree node will have the
64a9da330np	 * device_type information which is the one passed above. If it is
65a9da330np	 * NULL, check if the node has a PROM entry, and check the device_type
66a9da330np	 * in case of sparc. Else return NULL
67a9da330np	 */
68a9da330np	if (device_type == NULL) {
69a9da330np		/*
70a9da330np		 * Check the device type if it has a PROM entry. Because
71a9da330np		 * in sparc, the device_type entry will in the PROM node
72a9da330np		 */
73a9da330np		if (di_nodeid (node) == DI_PROM_NODEID) {
74a9da330np			phdl = di_prom_init ();
75a9da330np			if (phdl == DI_PROM_HANDLE_NIL) {
76a9da330np				HAL_ERROR (("Error in Initializing the PROM "
77a9da330np				    "handle to find cpu device: %s",
78a9da330np				    strerror (errno)));
79a9da330np				return (NULL);
80a9da330np			}
81a9da330np			if (di_prom_prop_lookup_strings (phdl, node,
82a9da330np			    "device_type", &prom_device_type) == -1) {
83a9da330np				di_prom_fini (phdl);
84a9da330np				return (NULL);
85a9da330np			}
86a9da330np			if (strcmp (prom_device_type, "cpu") != 0) {
87a9da330np				di_prom_fini (phdl);
88a9da330np				return (NULL);
89a9da330np			}
90a9da330np			/*
91a9da330np			 * Get cpuid if available
92a9da330np			 */
93a9da330np			if (di_prom_prop_lookup_ints (phdl, node,
94a9da330np			    "cpuid", &int_cpu_id) > 0) {
95a9da330np				cpu_id = *int_cpu_id;
96a9da330np			} else {
97a9da330np				/*
98a9da330np				 * There is no cpuid entry in this arch.Just
99a9da330np				 * increment the cpuid which will be the
100a9da330np				 * current instance
101a9da330np				 */
102a9da330np				++cpu_id;
103a9da330np			}
104a9da330np			di_prom_fini (phdl);
105a9da330np		} else {
106a9da330np			return (NULL);
107a9da330np		}
108a9da330np
109a9da330np	} else if (strcmp (device_type, "cpu") == 0) {
110a9da330np		/*
111a9da330np		 * This is a x86 arch, because software device tree node
112a9da330np		 * has the device_type entry for cpu. The "reg" property
113a9da330np		 * will have the cpuid. If not just increment the cpuid
114a9da330np		 * which will be the current cpu instance in the kstat
115a9da330np		 */
116a9da330np		if (di_prop_lookup_ints (DDI_DEV_T_ANY, node,
117a9da330np		    "reg", &int_cpu_id) > 0) {
118a9da330np			cpu_id = *int_cpu_id;
119a9da330np		} else {
120a9da330np			/*
121a9da330np			 * There is no cpuid entry in this arch. Just
122a9da330np			 * increment the cpuid which will be the
123a9da330np			 * current instance
124a9da330np			 */
125a9da330np			++cpu_id;
126a9da330np		}
127a9da330np
128a9da330np	} else {
129a9da330np		return (NULL);
130a9da330np	}
131a9da330np
132a9da330np	HAL_DEBUG (("CPUID=> %x", cpu_id));
133a9da330np
134a9da330np	d = hal_device_new ();
135a9da330np
136a9da330np	/*
137a9da330np	 * devinfo_set_default_properties () uses di_instance() as part of
138a9da330np	 * the udi. For some solaris devices like cpu di_instance() is not
139a9da330np	 * present and it returns -1. For the udi to be unique can use the
140a9da330np	 * cpu_id.
141a9da330np	 */
142a9da330np	hal_device_property_set_string (d, "info.parent",
143a9da330np	    "/org/freedesktop/Hal/devices/local");
144a9da330np
145a9da330np	/*
146a9da330np	 * If cpu driver is not installed, then devfs_path returned by
147a9da330np	 * libdevinfo will be same for all cpu's.
148a9da330np	 * Since HAL stores the devices in its tree based on the devfs_path,
149a9da330np	 * To make it unique, will be concatenating devfs_path with cpu_id
150a9da330np	 */
151a9da330np	if (di_driver_name (node) == NULL) {
152a9da330np		snprintf (cpu_devfs_path, HAL_PATH_MAX, "%s_%d",
153a9da330np		    devfs_path, cpu_id);
154a9da330np	} else {
155a9da330np		snprintf (cpu_devfs_path, HAL_PATH_MAX, "%s", devfs_path);
156a9da330np	}
157a9da330np
158a9da330np	HAL_DEBUG(("DevfsPath=> %s, CPUID=> %d", cpu_devfs_path, cpu_id));
159a9da330np
160a9da330np	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
161a9da330np	    "/org/freedesktop/Hal/devices%s_%d", cpu_devfs_path, cpu_id);
162a9da330np	hal_device_set_udi (d, udi);
163a9da330np	hal_device_property_set_string (d, "info.udi", udi);
164a9da330np	if (di_prop_lookup_strings (DDI_DEV_T_ANY, node, "model", &s) > 0) {
165a9da330np		hal_device_property_set_string (d, "info.product", s);
166a9da330np	} else {
167a9da330np		hal_device_property_set_string (d, "info.product",
168a9da330np		    di_node_name (node));
169a9da330np	}
170a9da330np	hal_device_property_set_string (d, "solaris.devfs_path",
171a9da330np	    cpu_devfs_path);
172a9da330np	if ((driver_name = di_driver_name (node)) != NULL) {
173a9da330np		hal_device_property_set_string (d, "info.solaris.driver",
174a9da330np		    driver_name);
175a9da330np	}
176a9da330np
177a9da330np	hal_device_add_capability (d, "processor");
178a9da330np
179a9da330np	hal_device_property_set_int (d, "processor.number", cpu_id);
180a9da330np
181a9da330np	/*
182a9da330np	 * Get the cpu related info from the kstat
183a9da330np	 */
184a9da330np	kc = kstat_open ();
185a9da330np	if (kc == NULL) {
186a9da330np		HAL_ERROR (("Could not open kstat to get cpu info: %s",
187a9da330np		    strerror (errno)));
188a9da330np		goto next;
189a9da330np	}
190a9da330np
191a9da330np	ksp = kstat_lookup (kc, "cpu_info", cpu_id, NULL);
192a9da330np	if (ksp == NULL) {
193a9da330np		HAL_ERROR (("Could not lookup kstat to get cpu info: %s",
194a9da330np		    strerror (errno)));
195a9da330np		if (kc) {
196a9da330np			kstat_close (kc);
197a9da330np		}
198a9da330np		return (NULL);
199a9da330np	}
200a9da330np
201a9da330np	kstat_read (kc, ksp, NULL);
202a9da330np	ksdata = (kstat_named_t *)kstat_data_lookup (ksp, "clock_MHz");
203a9da330np	if (ksdata == NULL) {
204a9da330np		HAL_ERROR (("Could not get kstat clock_MHz data for cpu: %s",
205a9da330np		    strerror (errno)));
206a9da330np		goto next;
207a9da330np	}
208a9da330np	clock_mhz = (uint64_t)ksdata->value.l;
209a9da330np
210a9da330np	if (hal_device_property_set_uint64 (d, "processor.maximum_speed",
211a9da330np	    clock_mhz) == FALSE) {
212a9da330np		HAL_INFO (("Could not set the processor speed device prop"));
213a9da330np	}
214a9da330np
215a9da330np
216a9da330np	ksdata = (kstat_named_t *)kstat_data_lookup (ksp,
217a9da330np	    "supported_frequencies_Hz");
218a9da330np	if (ksdata == NULL) {
219a9da330np		HAL_INFO (("Could not get kstat supported_frequencies_Hz data"
220a9da330np		    " for cpu: %s", strerror (errno)));
221a9da330np		is_supp_freqs = FALSE;
222a9da330np	} else {
223a9da330np		/*
224a9da330np		 * If more than one freq is supported, then they are seperated
225a9da330np		 * by a ":"
226a9da330np		 */
227a9da330np		if (strstr (ksdata->value.str.addr.ptr, ":") == NULL) {
228a9da330np			is_supp_freqs = FALSE;
229a9da330np		} else {
230a9da330np			is_supp_freqs = TRUE;
231a9da330np		}
232a9da330np	}
233a9da330np
234a9da330np	if (hal_device_property_set_bool (d, "processor.can_throttle",
235a9da330np	    is_supp_freqs) == FALSE) {
236a9da330np		HAL_INFO (("Could not set the processor.can_throttle"
237a9da330np		    " device prop"));
238a9da330np	}
239a9da330np
240a9da330npnext:
241a9da330np	if (kc) {
242a9da330np		kstat_close (kc);
243a9da330np	}
244a9da330np
245a9da330np	devinfo_add_enqueue (d, cpu_devfs_path, &devinfo_cpu_handler);
246a9da330np	return (d);
247a9da330np}
248