processor_throttling.c 8.4 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


	if (!pr)
60
		return -EINVAL;
L
Linus Torvalds 已提交
61 62

	if (!pr->flags.throttling)
63
		return -ENODEV;
L
Linus Torvalds 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

	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 已提交
84
			state = pr->throttling.state_count - duty_value;
L
Linus Torvalds 已提交
85 86 87 88 89 90 91
	}

	pr->throttling.state = state;

	local_irq_enable();

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

95
	return 0;
L
Linus Torvalds 已提交
96 97
}

L
Len Brown 已提交
98
int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
L
Linus Torvalds 已提交
99
{
L
Len Brown 已提交
100 101 102
	u32 value = 0;
	u32 duty_mask = 0;
	u32 duty_value = 0;
L
Linus Torvalds 已提交
103 104 105


	if (!pr)
106
		return -EINVAL;
L
Linus Torvalds 已提交
107 108

	if ((state < 0) || (state > (pr->throttling.state_count - 1)))
109
		return -EINVAL;
L
Linus Torvalds 已提交
110 111

	if (!pr->flags.throttling)
112
		return -ENODEV;
L
Linus Torvalds 已提交
113 114

	if (state == pr->throttling.state)
115
		return 0;
L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123 124 125 126 127

	/*
	 * 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;

128
		duty_mask <<= acpi_gbl_FADT.duty_offset;
L
Linus Torvalds 已提交
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
		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 已提交
162 163 164
			  "Throttling state set to T%d (%d%%)\n", state,
			  (pr->throttling.states[state].performance ? pr->
			   throttling.states[state].performance / 10 : 0)));
L
Linus Torvalds 已提交
165

166
	return 0;
L
Linus Torvalds 已提交
167 168
}

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


	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
177 178 179 180
			  "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 已提交
181 182

	if (!pr)
183
		return -EINVAL;
L
Linus Torvalds 已提交
184 185 186 187 188

	/* TBD: Support ACPI 2.0 objects */

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

	/*
	 * 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 已提交
207
				  "Throttling not supported on PIIX4 A- or B-step\n"));
208
		return 0;
L
Linus Torvalds 已提交
209 210
	}

211
	pr->throttling.state_count = 1 << acpi_gbl_FADT.duty_width;
L
Linus Torvalds 已提交
212 213 214 215 216 217 218 219 220

	/*
	 * 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 已提交
221
	for (i = 0; i < pr->throttling.state_count; i++) {
L
Linus Torvalds 已提交
222 223 224 225 226
		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 已提交
227
			  pr->throttling.state_count));
L
Linus Torvalds 已提交
228 229 230 231 232 233 234 235 236 237 238 239 240 241

	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 已提交
242 243 244
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
				  "Disabling throttling (was T%d)\n",
				  pr->throttling.state));
L
Linus Torvalds 已提交
245 246 247 248 249
		result = acpi_processor_set_throttling(pr, 0);
		if (result)
			goto end;
	}

L
Len Brown 已提交
250
      end:
L
Linus Torvalds 已提交
251 252 253
	if (result)
		pr->flags.throttling = 0;

254
	return result;
L
Linus Torvalds 已提交
255 256 257 258
}

/* proc interface */

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


	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 已提交
278 279
		seq_puts(seq,
			 "Could not determine current throttling state.\n");
L
Linus Torvalds 已提交
280 281 282 283
		goto end;
	}

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

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

L
Len Brown 已提交
294
      end:
295
	return 0;
L
Linus Torvalds 已提交
296 297
}

L
Len Brown 已提交
298 299
static int acpi_processor_throttling_open_fs(struct inode *inode,
					     struct file *file)
L
Linus Torvalds 已提交
300 301
{
	return single_open(file, acpi_processor_throttling_seq_show,
L
Len Brown 已提交
302
			   PDE(inode)->data);
L
Linus Torvalds 已提交
303 304
}

305 306 307
static ssize_t acpi_processor_write_throttling(struct file * file,
					       const char __user * buffer,
					       size_t count, loff_t * data)
L
Linus Torvalds 已提交
308
{
L
Len Brown 已提交
309
	int result = 0;
310 311
	struct seq_file *m = file->private_data;
	struct acpi_processor *pr = m->private;
L
Len Brown 已提交
312
	char state_string[12] = { '\0' };
L
Linus Torvalds 已提交
313 314 315


	if (!pr || (count > sizeof(state_string) - 1))
316
		return -EINVAL;
L
Linus Torvalds 已提交
317 318

	if (copy_from_user(state_string, buffer, count))
319
		return -EFAULT;
L
Linus Torvalds 已提交
320 321 322 323

	state_string[count] = '\0';

	result = acpi_processor_set_throttling(pr,
L
Len Brown 已提交
324 325
					       simple_strtoul(state_string,
							      NULL, 0));
L
Linus Torvalds 已提交
326
	if (result)
327
		return result;
L
Linus Torvalds 已提交
328

329
	return count;
L
Linus Torvalds 已提交
330 331 332
}

struct file_operations acpi_processor_throttling_fops = {
L
Len Brown 已提交
333 334
	.open = acpi_processor_throttling_open_fs,
	.read = seq_read,
335
	.write = acpi_processor_write_throttling,
L
Len Brown 已提交
336 337
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
338
};