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