• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/src/linux/linux-2.6/arch/powerpc/platforms/cell/
1/*
2 * thermal support for the cell processor
3 *
4 * This module adds some sysfs attributes to cpu and spu nodes.
5 * Base for measurements are the digital thermal sensors (DTS)
6 * located on the chip.
7 * The accuracy is 2 degrees, starting from 65 up to 125 degrees celsius
8 * The attributes can be found under
9 * /sys/devices/system/cpu/cpuX/thermal
10 * /sys/devices/system/spu/spuX/thermal
11 *
12 * The following attributes are added for each node:
13 * temperature:
14 *	contains the current temperature measured by the DTS
15 * throttle_begin:
16 *	throttling begins when temperature is greater or equal to
17 *	throttle_begin. Setting this value to 125 prevents throttling.
18 * throttle_end:
19 *	throttling is being ceased, if the temperature is lower than
20 *	throttle_end. Due to a delay between applying throttling and
21 *	a reduced temperature this value should be less than throttle_begin.
22 *	A value equal to throttle_begin provides only a very little hysteresis.
23 * throttle_full_stop:
24 *	If the temperatrue is greater or equal to throttle_full_stop,
25 *	full throttling is applied to the cpu or spu. This value should be
26 *	greater than throttle_begin and throttle_end. Setting this value to
27 *	65 prevents the unit from running code at all.
28 *
29 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
30 *
31 * Author: Christian Krafft <krafft@de.ibm.com>
32 *
33 * This program is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published by
35 * the Free Software Foundation; either version 2, or (at your option)
36 * any later version.
37 *
38 * This program is distributed in the hope that it will be useful,
39 * but WITHOUT ANY WARRANTY; without even the implied warranty of
40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41 * GNU General Public License for more details.
42 *
43 * You should have received a copy of the GNU General Public License
44 * along with this program; if not, write to the Free Software
45 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
46 */
47
48#include <linux/module.h>
49#include <linux/sysdev.h>
50#include <linux/kernel.h>
51#include <linux/cpu.h>
52#include <asm/spu.h>
53#include <asm/io.h>
54#include <asm/prom.h>
55
56#include "cbe_regs.h"
57#include "spu_priv1_mmio.h"
58
59#define TEMP_MIN 65
60#define TEMP_MAX 125
61
62#define SYSDEV_PREFIX_ATTR(_prefix,_name,_mode)			\
63struct sysdev_attribute attr_ ## _prefix ## _ ## _name = {	\
64	.attr = { .name = __stringify(_name), .mode = _mode },	\
65	.show	= _prefix ## _show_ ## _name,			\
66	.store	= _prefix ## _store_ ## _name,			\
67};
68
69static inline u8 reg_to_temp(u8 reg_value)
70{
71	return ((reg_value & 0x3f) << 1) + TEMP_MIN;
72}
73
74static inline u8 temp_to_reg(u8 temp)
75{
76	return ((temp - TEMP_MIN) >> 1) & 0x3f;
77}
78
79static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev)
80{
81	struct spu *spu;
82
83	spu = container_of(sysdev, struct spu, sysdev);
84
85	return cbe_get_pmd_regs(spu_devnode(spu));
86}
87
88/* returns the value for a given spu in a given register */
89static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg)
90{
91	const unsigned int *id;
92	union spe_reg value;
93	struct spu *spu;
94
95	/* getting the id from the reg attribute will not work on future device-tree layouts
96	 * in future we should store the id to the spu struct and use it here */
97	spu = container_of(sysdev, struct spu, sysdev);
98	id = of_get_property(spu_devnode(spu), "reg", NULL);
99	value.val = in_be64(&reg->val);
100
101	return value.spe[*id];
102}
103
104static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf)
105{
106	u8 value;
107	struct cbe_pmd_regs __iomem *pmd_regs;
108
109	pmd_regs = get_pmd_regs(sysdev);
110
111	value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1);
112
113	return sprintf(buf, "%d\n", reg_to_temp(value));
114}
115
116static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, int pos)
117{
118	u64 value;
119
120	value = in_be64(&pmd_regs->tm_tpr.val);
121	/* access the corresponding byte */
122	value >>= pos;
123	value &= 0x3F;
124
125	return sprintf(buf, "%d\n", reg_to_temp(value));
126}
127
128static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos)
129{
130	u64 reg_value;
131	int temp;
132	u64 new_value;
133	int ret;
134
135	ret = sscanf(buf, "%u", &temp);
136
137	if (ret != 1 || temp < TEMP_MIN || temp > TEMP_MAX)
138		return -EINVAL;
139
140	new_value = temp_to_reg(temp);
141
142	reg_value = in_be64(&pmd_regs->tm_tpr.val);
143
144	/* zero out bits for new value */
145	reg_value &= ~(0xffull << pos);
146	/* set bits to new value */
147	reg_value |= new_value << pos;
148
149	out_be64(&pmd_regs->tm_tpr.val, reg_value);
150	return size;
151}
152
153static ssize_t spu_show_throttle_end(struct sys_device *sysdev, char *buf)
154{
155	return show_throttle(get_pmd_regs(sysdev), buf, 0);
156}
157
158static ssize_t spu_show_throttle_begin(struct sys_device *sysdev, char *buf)
159{
160	return show_throttle(get_pmd_regs(sysdev), buf, 8);
161}
162
163static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev, char *buf)
164{
165	return show_throttle(get_pmd_regs(sysdev), buf, 16);
166}
167
168static ssize_t spu_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size)
169{
170	return store_throttle(get_pmd_regs(sysdev), buf, size, 0);
171}
172
173static ssize_t spu_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size)
174{
175	return store_throttle(get_pmd_regs(sysdev), buf, size, 8);
176}
177
178static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size)
179{
180	return store_throttle(get_pmd_regs(sysdev), buf, size, 16);
181}
182
183static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos)
184{
185	struct cbe_pmd_regs __iomem *pmd_regs;
186	u64 value;
187
188	pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);
189	value = in_be64(&pmd_regs->ts_ctsr2);
190
191	value = (value >> pos) & 0x3f;
192
193	return sprintf(buf, "%d\n", reg_to_temp(value));
194}
195
196
197/* shows the temperature of the DTS on the PPE,
198 * located near the linear thermal sensor */
199static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf)
200{
201	return ppe_show_temp(sysdev, buf, 32);
202}
203
204/* shows the temperature of the second DTS on the PPE */
205static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf)
206{
207	return ppe_show_temp(sysdev, buf, 0);
208}
209
210static ssize_t ppe_show_throttle_end(struct sys_device *sysdev, char *buf)
211{
212	return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32);
213}
214
215static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev, char *buf)
216{
217	return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40);
218}
219
220static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev, char *buf)
221{
222	return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48);
223}
224
225static ssize_t ppe_store_throttle_end(struct sys_device *sysdev, const char *buf, size_t size)
226{
227	return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32);
228}
229
230static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev, const char *buf, size_t size)
231{
232	return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40);
233}
234
235static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev, const char *buf, size_t size)
236{
237	return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48);
238}
239
240
241static struct sysdev_attribute attr_spu_temperature = {
242	.attr = {.name = "temperature", .mode = 0400 },
243	.show = spu_show_temp,
244};
245
246static SYSDEV_PREFIX_ATTR(spu, throttle_end, 0600);
247static SYSDEV_PREFIX_ATTR(spu, throttle_begin, 0600);
248static SYSDEV_PREFIX_ATTR(spu, throttle_full_stop, 0600);
249
250
251static struct attribute *spu_attributes[] = {
252	&attr_spu_temperature.attr,
253	&attr_spu_throttle_end.attr,
254	&attr_spu_throttle_begin.attr,
255	&attr_spu_throttle_full_stop.attr,
256	NULL,
257};
258
259static struct attribute_group spu_attribute_group = {
260	.name	= "thermal",
261	.attrs	= spu_attributes,
262};
263
264static struct sysdev_attribute attr_ppe_temperature0 = {
265	.attr = {.name = "temperature0", .mode = 0400 },
266	.show = ppe_show_temp0,
267};
268
269static struct sysdev_attribute attr_ppe_temperature1 = {
270	.attr = {.name = "temperature1", .mode = 0400 },
271	.show = ppe_show_temp1,
272};
273
274static SYSDEV_PREFIX_ATTR(ppe, throttle_end, 0600);
275static SYSDEV_PREFIX_ATTR(ppe, throttle_begin, 0600);
276static SYSDEV_PREFIX_ATTR(ppe, throttle_full_stop, 0600);
277
278static struct attribute *ppe_attributes[] = {
279	&attr_ppe_temperature0.attr,
280	&attr_ppe_temperature1.attr,
281	&attr_ppe_throttle_end.attr,
282	&attr_ppe_throttle_begin.attr,
283	&attr_ppe_throttle_full_stop.attr,
284	NULL,
285};
286
287static struct attribute_group ppe_attribute_group = {
288	.name	= "thermal",
289	.attrs	= ppe_attributes,
290};
291
292/*
293 * initialize throttling with default values
294 */
295static void __init init_default_values(void)
296{
297	int cpu;
298	struct cbe_pmd_regs __iomem *pmd_regs;
299	struct sys_device *sysdev;
300	union ppe_spe_reg tpr;
301	union spe_reg str1;
302	u64 str2;
303	union spe_reg cr1;
304	u64 cr2;
305
306	/* TPR defaults */
307	/* ppe
308	 *	1F - no full stop
309	 *	08 - dynamic throttling starts if over 80 degrees
310	 *	03 - dynamic throttling ceases if below 70 degrees */
311	tpr.ppe = 0x1F0803;
312	/* spe
313	 *	10 - full stopped when over 96 degrees
314	 *	08 - dynamic throttling starts if over 80 degrees
315	 *	03 - dynamic throttling ceases if below 70 degrees
316	 */
317	tpr.spe = 0x100803;
318
319	/* STR defaults */
320	/* str1
321	 *	10 - stop 16 of 32 cycles
322	 */
323	str1.val = 0x1010101010101010ull;
324	/* str2
325	 *	10 - stop 16 of 32 cycles
326	 */
327	str2 = 0x10;
328
329	/* CR defaults */
330	/* cr1
331	 *	4 - normal operation
332	 */
333	cr1.val = 0x0404040404040404ull;
334	/* cr2
335	 *	4 - normal operation
336	 */
337	cr2 = 0x04;
338
339	for_each_possible_cpu (cpu) {
340		pr_debug("processing cpu %d\n", cpu);
341		sysdev = get_cpu_sysdev(cpu);
342		pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);
343
344		out_be64(&pmd_regs->tm_str2, str2);
345		out_be64(&pmd_regs->tm_str1.val, str1.val);
346		out_be64(&pmd_regs->tm_tpr.val, tpr.val);
347		out_be64(&pmd_regs->tm_cr1.val, cr1.val);
348		out_be64(&pmd_regs->tm_cr2, cr2);
349	}
350}
351
352
353static int __init thermal_init(void)
354{
355	init_default_values();
356
357	spu_add_sysdev_attr_group(&spu_attribute_group);
358	cpu_add_sysdev_attr_group(&ppe_attribute_group);
359
360	return 0;
361}
362module_init(thermal_init);
363
364static void __exit thermal_exit(void)
365{
366	spu_remove_sysdev_attr_group(&spu_attribute_group);
367	cpu_remove_sysdev_attr_group(&ppe_attribute_group);
368}
369module_exit(thermal_exit);
370
371MODULE_LICENSE("GPL");
372MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
373