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