Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.24 384 lines 10 kB view raw
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) \ 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 union spe_reg value; 92 struct spu *spu; 93 94 spu = container_of(sysdev, struct spu, sysdev); 95 value.val = in_be64(&reg->val); 96 97 return value.spe[spu->spe_id]; 98} 99 100static 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 112static 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 124static 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 149static 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 154static 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 159static 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 164static 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 169static 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 174static 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 179static 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 */ 195static 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 */ 201static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf) 202{ 203 return ppe_show_temp(sysdev, buf, 0); 204} 205 206static 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 211static 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 216static 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 221static 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 226static 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 231static 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 237static struct sysdev_attribute attr_spu_temperature = { 238 .attr = {.name = "temperature", .mode = 0400 }, 239 .show = spu_show_temp, 240}; 241 242static SYSDEV_PREFIX_ATTR(spu, throttle_end, 0600); 243static SYSDEV_PREFIX_ATTR(spu, throttle_begin, 0600); 244static SYSDEV_PREFIX_ATTR(spu, throttle_full_stop, 0600); 245 246 247static 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 255static struct attribute_group spu_attribute_group = { 256 .name = "thermal", 257 .attrs = spu_attributes, 258}; 259 260static struct sysdev_attribute attr_ppe_temperature0 = { 261 .attr = {.name = "temperature0", .mode = 0400 }, 262 .show = ppe_show_temp0, 263}; 264 265static struct sysdev_attribute attr_ppe_temperature1 = { 266 .attr = {.name = "temperature1", .mode = 0400 }, 267 .show = ppe_show_temp1, 268}; 269 270static SYSDEV_PREFIX_ATTR(ppe, throttle_end, 0600); 271static SYSDEV_PREFIX_ATTR(ppe, throttle_begin, 0600); 272static SYSDEV_PREFIX_ATTR(ppe, throttle_full_stop, 0600); 273 274static 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 283static struct attribute_group ppe_attribute_group = { 284 .name = "thermal", 285 .attrs = ppe_attributes, 286}; 287 288/* 289 * initialize throttling with default values 290 */ 291static 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 362static 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} 373module_init(thermal_init); 374 375static 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} 380module_exit(thermal_exit); 381 382MODULE_LICENSE("GPL"); 383MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); 384