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
/*
 * 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 _COMPONENT              ACPI_PROCESSOR_COMPONENT
45
ACPI_MODULE_NAME("processor_throttling");
L
Linus Torvalds 已提交
46 47 48 49

/* --------------------------------------------------------------------------
                              Throttling Control
   -------------------------------------------------------------------------- */
L
Len Brown 已提交
50
static int acpi_processor_get_throttling(struct acpi_processor *pr)
L
Linus Torvalds 已提交
51
{
L
Len Brown 已提交
52 53 54 55
	int state = 0;
	u32 value = 0;
	u32 duty_mask = 0;
	u32 duty_value = 0;
L
Linus Torvalds 已提交
56 57 58


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

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

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

	pr->throttling.state = state;

	local_irq_enable();

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

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

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


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

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

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

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

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

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

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

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


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

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

	/* TBD: Support ACPI 2.0 objects */

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

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

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

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

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

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

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

/* proc interface */

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


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

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

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

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

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

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


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

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

	state_string[count] = '\0';

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

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

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