]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - arch/powerpc/platforms/cell/cbe_thermal.c
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152
[mirror_ubuntu-jammy-kernel.git] / arch / powerpc / platforms / cell / cbe_thermal.c
CommitLineData
b3d7dc19
CK
1/*
2 * thermal support for the cell processor
3 *
5f7bdaee
CK
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 *
b3d7dc19
CK
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>
8a25a2fd 49#include <linux/device.h>
b3d7dc19
CK
50#include <linux/kernel.h>
51#include <linux/cpu.h>
5c35a02c 52#include <linux/stringify.h>
b3d7dc19
CK
53#include <asm/spu.h>
54#include <asm/io.h>
55#include <asm/prom.h>
eef686a0 56#include <asm/cell-regs.h>
b3d7dc19 57
e28b0031 58#include "spu_priv1_mmio.h"
b3d7dc19 59
5f7bdaee
CK
60#define TEMP_MIN 65
61#define TEMP_MAX 125
62
8a25a2fd
KS
63#define DEVICE_PREFIX_ATTR(_prefix,_name,_mode) \
64struct device_attribute attr_ ## _prefix ## _ ## _name = { \
5f7bdaee
CK
65 .attr = { .name = __stringify(_name), .mode = _mode }, \
66 .show = _prefix ## _show_ ## _name, \
67 .store = _prefix ## _store_ ## _name, \
68};
69
24d560d7
CK
70static inline u8 reg_to_temp(u8 reg_value)
71{
5f7bdaee
CK
72 return ((reg_value & 0x3f) << 1) + TEMP_MIN;
73}
74
75static inline u8 temp_to_reg(u8 temp)
76{
77 return ((temp - TEMP_MIN) >> 1) & 0x3f;
24d560d7
CK
78}
79
8a25a2fd 80static struct cbe_pmd_regs __iomem *get_pmd_regs(struct device *dev)
b3d7dc19
CK
81{
82 struct spu *spu;
83
8a25a2fd 84 spu = container_of(dev, struct spu, dev);
b3d7dc19 85
e28b0031 86 return cbe_get_pmd_regs(spu_devnode(spu));
b3d7dc19
CK
87}
88
89/* returns the value for a given spu in a given register */
8a25a2fd 90static u8 spu_read_register_value(struct device *dev, union spe_reg __iomem *reg)
b3d7dc19 91{
b3d7dc19
CK
92 union spe_reg value;
93 struct spu *spu;
94
8a25a2fd 95 spu = container_of(dev, struct spu, dev);
b3d7dc19
CK
96 value.val = in_be64(&reg->val);
97
fa7f374b 98 return value.spe[spu->spe_id];
b3d7dc19
CK
99}
100
8a25a2fd 101static ssize_t spu_show_temp(struct device *dev, struct device_attribute *attr,
4a0b2b4d 102 char *buf)
b3d7dc19 103{
24d560d7 104 u8 value;
b3d7dc19
CK
105 struct cbe_pmd_regs __iomem *pmd_regs;
106
8a25a2fd 107 pmd_regs = get_pmd_regs(dev);
b3d7dc19 108
8a25a2fd 109 value = spu_read_register_value(dev, &pmd_regs->ts_ctsr1);
24d560d7
CK
110
111 return sprintf(buf, "%d\n", reg_to_temp(value));
b3d7dc19
CK
112}
113
5f7bdaee
CK
114static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, int pos)
115{
116 u64 value;
117
118 value = in_be64(&pmd_regs->tm_tpr.val);
119 /* access the corresponding byte */
120 value >>= pos;
121 value &= 0x3F;
122
123 return sprintf(buf, "%d\n", reg_to_temp(value));
124}
125
126static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos)
127{
128 u64 reg_value;
f5fc8229 129 unsigned int temp;
5f7bdaee
CK
130 u64 new_value;
131 int ret;
132
133 ret = sscanf(buf, "%u", &temp);
134
135 if (ret != 1 || temp < TEMP_MIN || temp > TEMP_MAX)
136 return -EINVAL;
137
138 new_value = temp_to_reg(temp);
139
140 reg_value = in_be64(&pmd_regs->tm_tpr.val);
141
142 /* zero out bits for new value */
143 reg_value &= ~(0xffull << pos);
144 /* set bits to new value */
145 reg_value |= new_value << pos;
146
147 out_be64(&pmd_regs->tm_tpr.val, reg_value);
148 return size;
149}
150
8a25a2fd
KS
151static ssize_t spu_show_throttle_end(struct device *dev,
152 struct device_attribute *attr, char *buf)
5f7bdaee 153{
8a25a2fd 154 return show_throttle(get_pmd_regs(dev), buf, 0);
5f7bdaee
CK
155}
156
8a25a2fd
KS
157static ssize_t spu_show_throttle_begin(struct device *dev,
158 struct device_attribute *attr, char *buf)
5f7bdaee 159{
8a25a2fd 160 return show_throttle(get_pmd_regs(dev), buf, 8);
5f7bdaee
CK
161}
162
8a25a2fd
KS
163static ssize_t spu_show_throttle_full_stop(struct device *dev,
164 struct device_attribute *attr, char *buf)
5f7bdaee 165{
8a25a2fd 166 return show_throttle(get_pmd_regs(dev), buf, 16);
5f7bdaee
CK
167}
168
8a25a2fd
KS
169static ssize_t spu_store_throttle_end(struct device *dev,
170 struct device_attribute *attr, const char *buf, size_t size)
5f7bdaee 171{
8a25a2fd 172 return store_throttle(get_pmd_regs(dev), buf, size, 0);
5f7bdaee
CK
173}
174
8a25a2fd
KS
175static ssize_t spu_store_throttle_begin(struct device *dev,
176 struct device_attribute *attr, const char *buf, size_t size)
5f7bdaee 177{
8a25a2fd 178 return store_throttle(get_pmd_regs(dev), buf, size, 8);
5f7bdaee
CK
179}
180
8a25a2fd
KS
181static ssize_t spu_store_throttle_full_stop(struct device *dev,
182 struct device_attribute *attr, const char *buf, size_t size)
5f7bdaee 183{
8a25a2fd 184 return store_throttle(get_pmd_regs(dev), buf, size, 16);
5f7bdaee
CK
185}
186
8a25a2fd 187static ssize_t ppe_show_temp(struct device *dev, char *buf, int pos)
b3d7dc19
CK
188{
189 struct cbe_pmd_regs __iomem *pmd_regs;
190 u64 value;
191
8a25a2fd 192 pmd_regs = cbe_get_cpu_pmd_regs(dev->id);
b3d7dc19
CK
193 value = in_be64(&pmd_regs->ts_ctsr2);
194
24d560d7 195 value = (value >> pos) & 0x3f;
b3d7dc19 196
24d560d7 197 return sprintf(buf, "%d\n", reg_to_temp(value));
b3d7dc19
CK
198}
199
200
201/* shows the temperature of the DTS on the PPE,
202 * located near the linear thermal sensor */
8a25a2fd
KS
203static ssize_t ppe_show_temp0(struct device *dev,
204 struct device_attribute *attr, char *buf)
b3d7dc19 205{
8a25a2fd 206 return ppe_show_temp(dev, buf, 32);
b3d7dc19
CK
207}
208
209/* shows the temperature of the second DTS on the PPE */
8a25a2fd
KS
210static ssize_t ppe_show_temp1(struct device *dev,
211 struct device_attribute *attr, char *buf)
b3d7dc19 212{
8a25a2fd 213 return ppe_show_temp(dev, buf, 0);
b3d7dc19
CK
214}
215
8a25a2fd
KS
216static ssize_t ppe_show_throttle_end(struct device *dev,
217 struct device_attribute *attr, char *buf)
5f7bdaee 218{
8a25a2fd 219 return show_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, 32);
5f7bdaee
CK
220}
221
8a25a2fd
KS
222static ssize_t ppe_show_throttle_begin(struct device *dev,
223 struct device_attribute *attr, char *buf)
5f7bdaee 224{
8a25a2fd 225 return show_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, 40);
5f7bdaee
CK
226}
227
8a25a2fd
KS
228static ssize_t ppe_show_throttle_full_stop(struct device *dev,
229 struct device_attribute *attr, char *buf)
5f7bdaee 230{
8a25a2fd 231 return show_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, 48);
5f7bdaee
CK
232}
233
8a25a2fd
KS
234static ssize_t ppe_store_throttle_end(struct device *dev,
235 struct device_attribute *attr, const char *buf, size_t size)
5f7bdaee 236{
8a25a2fd 237 return store_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, size, 32);
5f7bdaee
CK
238}
239
8a25a2fd
KS
240static ssize_t ppe_store_throttle_begin(struct device *dev,
241 struct device_attribute *attr, const char *buf, size_t size)
5f7bdaee 242{
8a25a2fd 243 return store_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, size, 40);
5f7bdaee
CK
244}
245
8a25a2fd
KS
246static ssize_t ppe_store_throttle_full_stop(struct device *dev,
247 struct device_attribute *attr, const char *buf, size_t size)
5f7bdaee 248{
8a25a2fd 249 return store_throttle(cbe_get_cpu_pmd_regs(dev->id), buf, size, 48);
5f7bdaee
CK
250}
251
252
8a25a2fd 253static struct device_attribute attr_spu_temperature = {
b3d7dc19
CK
254 .attr = {.name = "temperature", .mode = 0400 },
255 .show = spu_show_temp,
256};
257
8a25a2fd
KS
258static DEVICE_PREFIX_ATTR(spu, throttle_end, 0600);
259static DEVICE_PREFIX_ATTR(spu, throttle_begin, 0600);
260static DEVICE_PREFIX_ATTR(spu, throttle_full_stop, 0600);
5f7bdaee
CK
261
262
b3d7dc19
CK
263static struct attribute *spu_attributes[] = {
264 &attr_spu_temperature.attr,
5f7bdaee
CK
265 &attr_spu_throttle_end.attr,
266 &attr_spu_throttle_begin.attr,
267 &attr_spu_throttle_full_stop.attr,
22b6e590 268 NULL,
b3d7dc19
CK
269};
270
271static struct attribute_group spu_attribute_group = {
272 .name = "thermal",
273 .attrs = spu_attributes,
274};
275
8a25a2fd 276static struct device_attribute attr_ppe_temperature0 = {
b3d7dc19
CK
277 .attr = {.name = "temperature0", .mode = 0400 },
278 .show = ppe_show_temp0,
279};
280
8a25a2fd 281static struct device_attribute attr_ppe_temperature1 = {
b3d7dc19
CK
282 .attr = {.name = "temperature1", .mode = 0400 },
283 .show = ppe_show_temp1,
284};
285
8a25a2fd
KS
286static DEVICE_PREFIX_ATTR(ppe, throttle_end, 0600);
287static DEVICE_PREFIX_ATTR(ppe, throttle_begin, 0600);
288static DEVICE_PREFIX_ATTR(ppe, throttle_full_stop, 0600);
5f7bdaee 289
b3d7dc19
CK
290static struct attribute *ppe_attributes[] = {
291 &attr_ppe_temperature0.attr,
292 &attr_ppe_temperature1.attr,
5f7bdaee
CK
293 &attr_ppe_throttle_end.attr,
294 &attr_ppe_throttle_begin.attr,
295 &attr_ppe_throttle_full_stop.attr,
22b6e590 296 NULL,
b3d7dc19
CK
297};
298
299static struct attribute_group ppe_attribute_group = {
300 .name = "thermal",
301 .attrs = ppe_attributes,
302};
303
304/*
305 * initialize throttling with default values
306 */
827e3648 307static int __init init_default_values(void)
b3d7dc19
CK
308{
309 int cpu;
310 struct cbe_pmd_regs __iomem *pmd_regs;
8a25a2fd 311 struct device *dev;
b3d7dc19
CK
312 union ppe_spe_reg tpr;
313 union spe_reg str1;
314 u64 str2;
315 union spe_reg cr1;
316 u64 cr2;
317
318 /* TPR defaults */
319 /* ppe
320 * 1F - no full stop
321 * 08 - dynamic throttling starts if over 80 degrees
322 * 03 - dynamic throttling ceases if below 70 degrees */
323 tpr.ppe = 0x1F0803;
324 /* spe
325 * 10 - full stopped when over 96 degrees
326 * 08 - dynamic throttling starts if over 80 degrees
327 * 03 - dynamic throttling ceases if below 70 degrees
328 */
329 tpr.spe = 0x100803;
330
331 /* STR defaults */
332 /* str1
333 * 10 - stop 16 of 32 cycles
334 */
335 str1.val = 0x1010101010101010ull;
336 /* str2
337 * 10 - stop 16 of 32 cycles
338 */
339 str2 = 0x10;
340
341 /* CR defaults */
342 /* cr1
343 * 4 - normal operation
344 */
345 cr1.val = 0x0404040404040404ull;
346 /* cr2
347 * 4 - normal operation
348 */
349 cr2 = 0x04;
350
351 for_each_possible_cpu (cpu) {
352 pr_debug("processing cpu %d\n", cpu);
8a25a2fd 353 dev = get_cpu_device(cpu);
827e3648 354
8a25a2fd
KS
355 if (!dev) {
356 pr_info("invalid dev pointer for cbe_thermal\n");
827e3648
JCD
357 return -EINVAL;
358 }
359
8a25a2fd 360 pmd_regs = cbe_get_cpu_pmd_regs(dev->id);
b3d7dc19 361
827e3648
JCD
362 if (!pmd_regs) {
363 pr_info("invalid CBE regs pointer for cbe_thermal\n");
364 return -EINVAL;
365 }
366
b3d7dc19
CK
367 out_be64(&pmd_regs->tm_str2, str2);
368 out_be64(&pmd_regs->tm_str1.val, str1.val);
369 out_be64(&pmd_regs->tm_tpr.val, tpr.val);
370 out_be64(&pmd_regs->tm_cr1.val, cr1.val);
371 out_be64(&pmd_regs->tm_cr2, cr2);
372 }
827e3648
JCD
373
374 return 0;
b3d7dc19
CK
375}
376
377
378static int __init thermal_init(void)
379{
827e3648 380 int rc = init_default_values();
b3d7dc19 381
827e3648 382 if (rc == 0) {
8a25a2fd
KS
383 spu_add_dev_attr_group(&spu_attribute_group);
384 cpu_add_dev_attr_group(&ppe_attribute_group);
827e3648 385 }
b3d7dc19 386
827e3648 387 return rc;
b3d7dc19
CK
388}
389module_init(thermal_init);
390
391static void __exit thermal_exit(void)
392{
8a25a2fd
KS
393 spu_remove_dev_attr_group(&spu_attribute_group);
394 cpu_remove_dev_attr_group(&ppe_attribute_group);
b3d7dc19
CK
395}
396module_exit(thermal_exit);
397
398MODULE_LICENSE("GPL");
399MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
400