summaryrefslogtreecommitdiff
path: root/usr/src/uts/i86pc/os/cpupm.c
blob: 9a5112d9d71bcf09c0686452998703677a7b2e50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/cpupm.h>

/*
 * This callback is used to build the PPM CPU domains once
 * all the CPU devices have been started. The callback is
 * initialized by the PPM driver to point to a routine that
 * will build the domains.
 */
void (*cpupm_rebuild_cpu_domains)(void);

/*
 * This callback is used to reset the topspeed for all the
 * CPU devices. The callback is initialized by the PPM driver to
 * point to a routine that will reinitialize all the CPU devices
 * once all the CPU devices have been started and the CPU domains
 * built.
 */
void (*cpupm_init_topspeed)(void);

/*
 * This callback is used to redefine the topspeed for a CPU device.
 * Since all CPUs in a domain should have identical properties, this
 * callback is initialized by the PPM driver to point to a routine
 * that will redefine the topspeed for all devices in a CPU domain.
 * This callback is exercised whenever an ACPI _PPC change notification
 * is received by the CPU driver.
 */
void (*cpupm_redefine_topspeed)(void *);

/*
 * This callback is used by the PPM driver to call into the CPU driver
 * to find a CPU's current topspeed (i.e., it's current ACPI _PPC value).
 */
void (*cpupm_set_topspeed)(void *, int);

/*
 * This callback is used by the PPM driver to call into the CPU driver
 * to set a new topspeed for a CPU.
 */
int (*cpupm_get_topspeed)(void *);

/*
 * Used to dynamically keep track of the CPU dependencies as CPU
 * devices attach. Will subsequently be used by the PPM driver
 * to build PPM CPU domains.
 */
static cpupm_cpu_dependency_t *cpupm_cpu_dependencies = NULL;

/*
 * If we are unable to correctly identify a dependency for any CPU, then
 * we punt and all CPUs are managed as one domain.
 */
static boolean_t cpupm_dependencies_valid = B_TRUE;

/*
 * If any CPU fails to attach, then cpupm is disabled for all CPUs.
 */
static boolean_t cpupm_enabled = B_TRUE;

/*
 * Until all CPUs have succesfully attached, we do not allow
 * power management.
 */
static boolean_t cpupm_ready = B_FALSE;

/*
 * Print the CPU dependencies.
 */
static void
cpupm_print_cpu_dependencies()
{
	cpupm_cpu_dependency_t *dptr;
	cpupm_cpu_node_t *nptr;

	for (dptr = cpupm_cpu_dependencies; dptr != NULL;
	    dptr = dptr->cd_next) {
		for (nptr = dptr->cd_cpu; nptr != NULL; nptr = nptr->cn_next) {
			int instance = ddi_get_instance(nptr->cn_dip);
			cmn_err(CE_NOTE,
			    "print_cpu_dependencies: dependency %d "
			    "instance %d\n", dptr->cd_dependency_id, instance);
		}
	}
}

/*
 * Used to retrieve the dependencies built during CPUs attaching.
 */
cpupm_cpu_dependency_t *
cpupm_get_cpu_dependencies()
{
	return (cpupm_cpu_dependencies);
}

/*
 * Build dependencies as CPUs attach. Note that we don't need to worry
 * about locking the dependency lists as concurrency is not an issue.
 * This routine relies on the fact that the CPU devices are attached
 * sequentially by a single thread.
 */
void
cpupm_add_cpu2dependency(dev_info_t *dip, int cpu_dependency)
{
	cpupm_cpu_dependency_t *dptr;
	cpupm_cpu_node_t *nptr;

	if (!cpupm_dependencies_valid)
		return;

	if (cpu_dependency == -1) {
		cpupm_free_cpu_dependencies();
		return;
	}

	for (dptr = cpupm_cpu_dependencies; dptr != NULL;
	    dptr = dptr->cd_next) {
		if (dptr->cd_dependency_id == cpu_dependency)
			break;
	}

	/* new dependency is created and linked at the head */
	if (dptr == NULL) {
		dptr = kmem_zalloc(sizeof (cpupm_cpu_dependency_t), KM_SLEEP);
		dptr->cd_dependency_id = cpu_dependency;
		dptr->cd_next = cpupm_cpu_dependencies;
		cpupm_cpu_dependencies = dptr;
	}

	/* new cpu is created and linked at head of dependency */
	nptr = kmem_zalloc(sizeof (cpupm_cpu_node_t), KM_SLEEP);
	nptr->cn_dip = dip;
	nptr->cn_next = dptr->cd_cpu;
	dptr->cd_cpu = nptr;
}

/*
 * Free the CPU dependencies.
 */
void
cpupm_free_cpu_dependencies()
{
	cpupm_cpu_dependency_t *this_dependency, *next_dependency;
	cpupm_cpu_node_t *this_node, *next_node;

	cpupm_dependencies_valid = B_FALSE;
	this_dependency = cpupm_cpu_dependencies;
	while (this_dependency != NULL) {
		next_dependency = this_dependency->cd_next;

		/* discard CPU node chain */
		this_node = this_dependency->cd_cpu;
		while (this_node != NULL) {
			next_node = this_node->cn_next;
			kmem_free((void *)this_node,
			    sizeof (cpupm_cpu_node_t));
			this_node = next_node;
		}
		kmem_free((void *)this_dependency,
		    sizeof (cpupm_cpu_dependency_t));
		this_dependency = next_dependency;
	}
	cpupm_cpu_dependencies = NULL;
}

/*
 * If all CPUs have attached successfully, then the CPUs are
 * ready for power management.
 */
boolean_t
cpupm_is_ready()
{
#ifndef	__xpv
	if (!cpupm_enabled)
		return (B_FALSE);
	return (cpupm_ready);
#else
	return (B_FALSE);
#endif
}

/*
 * By default, cpupm is enabled. But if there are any errors attaching
 * any of the CPU devices, then it is disabled.
 */
void
cpupm_enable(boolean_t enable)
{
	if (!enable)
		cpupm_free_cpu_dependencies();
	cpupm_enabled = enable;
}

/*
 * Once all CPUs have been started, the PPM driver should build CPU
 * domains and initialize the topspeed for all CPU devices.
 */
void
cpupm_post_startup()
{
#ifndef	__xpv
	/*
	 * The CPU domain built by the PPM during CPUs attaching
	 * should be rebuilt with the information retrieved from
	 * ACPI.
	 */
	if (cpupm_rebuild_cpu_domains != NULL)
		(*cpupm_rebuild_cpu_domains)();

	/*
	 * If CPU power management was disabled, then there
	 * is nothing to do.
	 */
	if (!cpupm_enabled)
		return;

	cpupm_ready = B_TRUE;

	if (cpupm_init_topspeed != NULL)
		(*cpupm_init_topspeed)();
#else
	cpupm_ready = B_TRUE;
#endif
}