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