processor_throttling.c 8.9 KB
Newer Older
L
Linus Torvalds 已提交
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
/*
 * processor_throttling.c - Throttling submodule of the ACPI processor driver
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
 *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
 *  			- Added processor hotplug support
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#include <asm/io.h>
#include <asm/uaccess.h>

#include <acpi/acpi_bus.h>
#include <acpi/processor.h>

#define ACPI_PROCESSOR_COMPONENT        0x01000000
#define ACPI_PROCESSOR_CLASS            "processor"
#define ACPI_PROCESSOR_DRIVER_NAME      "ACPI Processor Driver"
#define _COMPONENT              ACPI_PROCESSOR_COMPONENT
L
Len Brown 已提交
46
ACPI_MODULE_NAME("acpi_processor")
L
Linus Torvalds 已提交
47 48 49 50

/* --------------------------------------------------------------------------
                              Throttling Control
   -------------------------------------------------------------------------- */
L
Len Brown 已提交
51
static int acpi_processor_get_throttling(struct acpi_processor *pr)
L
Linus Torvalds 已提交
52
{
L
Len Brown 已提交
53 54 55 56
	int state = 0;
	u32 value = 0;
	u32 duty_mask = 0;
	u32 duty_value = 0;
L
Linus Torvalds 已提交
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

	ACPI_FUNCTION_TRACE("acpi_processor_get_throttling");

	if (!pr)
		return_VALUE(-EINVAL);

	if (!pr->flags.throttling)
		return_VALUE(-ENODEV);

	pr->throttling.state = 0;

	duty_mask = pr->throttling.state_count - 1;

	duty_mask <<= pr->throttling.duty_offset;

	local_irq_disable();

	value = inl(pr->throttling.address);

	/*
	 * Compute the current throttling state when throttling is enabled
	 * (bit 4 is on).
	 */
	if (value & 0x10) {
		duty_value = value & duty_mask;
		duty_value >>= pr->throttling.duty_offset;

		if (duty_value)
L
Len Brown 已提交
85
			state = pr->throttling.state_count - duty_value;
L
Linus Torvalds 已提交
86 87 88 89 90 91 92
	}

	pr->throttling.state = state;

	local_irq_enable();

	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
93 94
			  "Throttling state is T%d (%d%% throttling applied)\n",
			  state, pr->throttling.states[state].performance));
L
Linus Torvalds 已提交
95 96 97 98

	return_VALUE(0);
}

L
Len Brown 已提交
99
int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
L
Linus Torvalds 已提交
100
{
L
Len Brown 已提交
101 102 103
	u32 value = 0;
	u32 duty_mask = 0;
	u32 duty_value = 0;
L
Linus Torvalds 已提交
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

	ACPI_FUNCTION_TRACE("acpi_processor_set_throttling");

	if (!pr)
		return_VALUE(-EINVAL);

	if ((state < 0) || (state > (pr->throttling.state_count - 1)))
		return_VALUE(-EINVAL);

	if (!pr->flags.throttling)
		return_VALUE(-ENODEV);

	if (state == pr->throttling.state)
		return_VALUE(0);

	/*
	 * Calculate the duty_value and duty_mask.
	 */
	if (state) {
		duty_value = pr->throttling.state_count - state;

		duty_value <<= pr->throttling.duty_offset;

		/* Used to clear all duty_value bits */
		duty_mask = pr->throttling.state_count - 1;

		duty_mask <<= acpi_fadt.duty_offset;
		duty_mask = ~duty_mask;
	}

	local_irq_disable();

	/*
	 * Disable throttling by writing a 0 to bit 4.  Note that we must
	 * turn it off before you can change the duty_value.
	 */
	value = inl(pr->throttling.address);
	if (value & 0x10) {
		value &= 0xFFFFFFEF;
		outl(value, pr->throttling.address);
	}

	/*
	 * Write the new duty_value and then enable throttling.  Note
	 * that a state value of 0 leaves throttling disabled.
	 */
	if (state) {
		value &= duty_mask;
		value |= duty_value;
		outl(value, pr->throttling.address);

		value |= 0x00000010;
		outl(value, pr->throttling.address);
	}

	pr->throttling.state = state;

	local_irq_enable();

	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
164 165 166
			  "Throttling state set to T%d (%d%%)\n", state,
			  (pr->throttling.states[state].performance ? pr->
			   throttling.states[state].performance / 10 : 0)));
L
Linus Torvalds 已提交
167 168 169 170

	return_VALUE(0);
}

L
Len Brown 已提交
171
int acpi_processor_get_throttling_info(struct acpi_processor *pr)
L
Linus Torvalds 已提交
172
{
L
Len Brown 已提交
173 174 175
	int result = 0;
	int step = 0;
	int i = 0;
L
Linus Torvalds 已提交
176 177 178 179

	ACPI_FUNCTION_TRACE("acpi_processor_get_throttling_info");

	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
180 181 182 183
			  "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n",
			  pr->throttling.address,
			  pr->throttling.duty_offset,
			  pr->throttling.duty_width));
L
Linus Torvalds 已提交
184 185 186 187 188 189 190 191 192

	if (!pr)
		return_VALUE(-EINVAL);

	/* TBD: Support ACPI 2.0 objects */

	if (!pr->throttling.address) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n"));
		return_VALUE(0);
L
Len Brown 已提交
193
	} else if (!pr->throttling.duty_width) {
L
Linus Torvalds 已提交
194 195 196 197
		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling states\n"));
		return_VALUE(0);
	}
	/* TBD: Support duty_cycle values that span bit 4. */
L
Len Brown 已提交
198
	else if ((pr->throttling.duty_offset + pr->throttling.duty_width) > 4) {
199
		ACPI_WARNING((AE_INFO, "duty_cycle spans bit 4"));
L
Linus Torvalds 已提交
200 201 202 203 204 205 206 207 208 209
		return_VALUE(0);
	}

	/*
	 * PIIX4 Errata: We don't support throttling on the original PIIX4.
	 * This shouldn't be an issue as few (if any) mobile systems ever
	 * used this part.
	 */
	if (errata.piix4.throttle) {
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
210
				  "Throttling not supported on PIIX4 A- or B-step\n"));
L
Linus Torvalds 已提交
211 212 213 214 215 216 217 218 219 220 221 222 223
		return_VALUE(0);
	}

	pr->throttling.state_count = 1 << acpi_fadt.duty_width;

	/*
	 * Compute state values. Note that throttling displays a linear power/
	 * performance relationship (at 50% performance the CPU will consume
	 * 50% power).  Values are in 1/10th of a percent to preserve accuracy.
	 */

	step = (1000 / pr->throttling.state_count);

L
Len Brown 已提交
224
	for (i = 0; i < pr->throttling.state_count; i++) {
L
Linus Torvalds 已提交
225 226 227 228 229
		pr->throttling.states[i].performance = step * i;
		pr->throttling.states[i].power = step * i;
	}

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n",
L
Len Brown 已提交
230
			  pr->throttling.state_count));
L
Linus Torvalds 已提交
231 232 233 234 235 236 237 238 239 240 241 242 243 244

	pr->flags.throttling = 1;

	/*
	 * Disable throttling (if enabled).  We'll let subsequent policy (e.g.
	 * thermal) decide to lower performance if it so chooses, but for now
	 * we'll crank up the speed.
	 */

	result = acpi_processor_get_throttling(pr);
	if (result)
		goto end;

	if (pr->throttling.state) {
L
Len Brown 已提交
245 246 247
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Disabling throttling (was T%d)\n",
				  pr->throttling.state));
L
Linus Torvalds 已提交
248 249 250 251 252
		result = acpi_processor_set_throttling(pr, 0);
		if (result)
			goto end;
	}

L
Len Brown 已提交
253
      end:
L
Linus Torvalds 已提交
254 255 256 257 258 259 260 261
	if (result)
		pr->flags.throttling = 0;

	return_VALUE(result);
}

/* proc interface */

L
Len Brown 已提交
262 263
static int acpi_processor_throttling_seq_show(struct seq_file *seq,
					      void *offset)
L
Linus Torvalds 已提交
264
{
L
Len Brown 已提交
265 266 267
	struct acpi_processor *pr = (struct acpi_processor *)seq->private;
	int i = 0;
	int result = 0;
L
Linus Torvalds 已提交
268 269 270 271 272 273 274 275 276 277 278 279 280 281

	ACPI_FUNCTION_TRACE("acpi_processor_throttling_seq_show");

	if (!pr)
		goto end;

	if (!(pr->throttling.state_count > 0)) {
		seq_puts(seq, "<not supported>\n");
		goto end;
	}

	result = acpi_processor_get_throttling(pr);

	if (result) {
L
Len Brown 已提交
282 283
		seq_puts(seq,
			 "Could not determine current throttling state.\n");
L
Linus Torvalds 已提交
284 285 286 287
		goto end;
	}

	seq_printf(seq, "state count:             %d\n"
L
Len Brown 已提交
288 289
		   "active state:            T%d\n",
		   pr->throttling.state_count, pr->throttling.state);
L
Linus Torvalds 已提交
290 291 292 293

	seq_puts(seq, "states:\n");
	for (i = 0; i < pr->throttling.state_count; i++)
		seq_printf(seq, "   %cT%d:                  %02d%%\n",
L
Len Brown 已提交
294 295 296
			   (i == pr->throttling.state ? '*' : ' '), i,
			   (pr->throttling.states[i].performance ? pr->
			    throttling.states[i].performance / 10 : 0));
L
Linus Torvalds 已提交
297

L
Len Brown 已提交
298
      end:
L
Linus Torvalds 已提交
299 300 301
	return_VALUE(0);
}

L
Len Brown 已提交
302 303
static int acpi_processor_throttling_open_fs(struct inode *inode,
					     struct file *file)
L
Linus Torvalds 已提交
304 305
{
	return single_open(file, acpi_processor_throttling_seq_show,
L
Len Brown 已提交
306
			   PDE(inode)->data);
L
Linus Torvalds 已提交
307 308
}

309 310 311
static ssize_t acpi_processor_write_throttling(struct file * file,
					       const char __user * buffer,
					       size_t count, loff_t * data)
L
Linus Torvalds 已提交
312
{
L
Len Brown 已提交
313 314 315 316
	int result = 0;
	struct seq_file *m = (struct seq_file *)file->private_data;
	struct acpi_processor *pr = (struct acpi_processor *)m->private;
	char state_string[12] = { '\0' };
L
Linus Torvalds 已提交
317 318 319 320 321 322 323 324 325 326 327 328

	ACPI_FUNCTION_TRACE("acpi_processor_write_throttling");

	if (!pr || (count > sizeof(state_string) - 1))
		return_VALUE(-EINVAL);

	if (copy_from_user(state_string, buffer, count))
		return_VALUE(-EFAULT);

	state_string[count] = '\0';

	result = acpi_processor_set_throttling(pr,
L
Len Brown 已提交
329 330
					       simple_strtoul(state_string,
							      NULL, 0));
L
Linus Torvalds 已提交
331 332 333 334 335 336 337
	if (result)
		return_VALUE(result);

	return_VALUE(count);
}

struct file_operations acpi_processor_throttling_fops = {
L
Len Brown 已提交
338 339
	.open = acpi_processor_throttling_open_fs,
	.read = seq_read,
340
	.write = acpi_processor_write_throttling,
L
Len Brown 已提交
341 342
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
343
};