Path: blob/master/arch/powerpc/platforms/cell/cbe_thermal.c
10818 views
/*1* thermal support for the cell processor2*3* This module adds some sysfs attributes to cpu and spu nodes.4* Base for measurements are the digital thermal sensors (DTS)5* located on the chip.6* The accuracy is 2 degrees, starting from 65 up to 125 degrees celsius7* The attributes can be found under8* /sys/devices/system/cpu/cpuX/thermal9* /sys/devices/system/spu/spuX/thermal10*11* The following attributes are added for each node:12* temperature:13* contains the current temperature measured by the DTS14* throttle_begin:15* throttling begins when temperature is greater or equal to16* throttle_begin. Setting this value to 125 prevents throttling.17* throttle_end:18* throttling is being ceased, if the temperature is lower than19* throttle_end. Due to a delay between applying throttling and20* a reduced temperature this value should be less than throttle_begin.21* A value equal to throttle_begin provides only a very little hysteresis.22* throttle_full_stop:23* If the temperatrue is greater or equal to throttle_full_stop,24* full throttling is applied to the cpu or spu. This value should be25* greater than throttle_begin and throttle_end. Setting this value to26* 65 prevents the unit from running code at all.27*28* (C) Copyright IBM Deutschland Entwicklung GmbH 200529*30* Author: Christian Krafft <[email protected]>31*32* This program is free software; you can redistribute it and/or modify33* it under the terms of the GNU General Public License as published by34* the Free Software Foundation; either version 2, or (at your option)35* any later version.36*37* This program is distributed in the hope that it will be useful,38* but WITHOUT ANY WARRANTY; without even the implied warranty of39* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the40* GNU General Public License for more details.41*42* You should have received a copy of the GNU General Public License43* along with this program; if not, write to the Free Software44* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.45*/4647#include <linux/module.h>48#include <linux/sysdev.h>49#include <linux/kernel.h>50#include <linux/cpu.h>51#include <asm/spu.h>52#include <asm/io.h>53#include <asm/prom.h>54#include <asm/cell-regs.h>5556#include "spu_priv1_mmio.h"5758#define TEMP_MIN 6559#define TEMP_MAX 1256061#define SYSDEV_PREFIX_ATTR(_prefix,_name,_mode) \62struct sysdev_attribute attr_ ## _prefix ## _ ## _name = { \63.attr = { .name = __stringify(_name), .mode = _mode }, \64.show = _prefix ## _show_ ## _name, \65.store = _prefix ## _store_ ## _name, \66};6768static inline u8 reg_to_temp(u8 reg_value)69{70return ((reg_value & 0x3f) << 1) + TEMP_MIN;71}7273static inline u8 temp_to_reg(u8 temp)74{75return ((temp - TEMP_MIN) >> 1) & 0x3f;76}7778static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev)79{80struct spu *spu;8182spu = container_of(sysdev, struct spu, sysdev);8384return cbe_get_pmd_regs(spu_devnode(spu));85}8687/* returns the value for a given spu in a given register */88static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg)89{90union spe_reg value;91struct spu *spu;9293spu = container_of(sysdev, struct spu, sysdev);94value.val = in_be64(®->val);9596return value.spe[spu->spe_id];97}9899static ssize_t spu_show_temp(struct sys_device *sysdev, struct sysdev_attribute *attr,100char *buf)101{102u8 value;103struct cbe_pmd_regs __iomem *pmd_regs;104105pmd_regs = get_pmd_regs(sysdev);106107value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1);108109return sprintf(buf, "%d\n", reg_to_temp(value));110}111112static ssize_t show_throttle(struct cbe_pmd_regs __iomem *pmd_regs, char *buf, int pos)113{114u64 value;115116value = in_be64(&pmd_regs->tm_tpr.val);117/* access the corresponding byte */118value >>= pos;119value &= 0x3F;120121return sprintf(buf, "%d\n", reg_to_temp(value));122}123124static ssize_t store_throttle(struct cbe_pmd_regs __iomem *pmd_regs, const char *buf, size_t size, int pos)125{126u64 reg_value;127int temp;128u64 new_value;129int ret;130131ret = sscanf(buf, "%u", &temp);132133if (ret != 1 || temp < TEMP_MIN || temp > TEMP_MAX)134return -EINVAL;135136new_value = temp_to_reg(temp);137138reg_value = in_be64(&pmd_regs->tm_tpr.val);139140/* zero out bits for new value */141reg_value &= ~(0xffull << pos);142/* set bits to new value */143reg_value |= new_value << pos;144145out_be64(&pmd_regs->tm_tpr.val, reg_value);146return size;147}148149static ssize_t spu_show_throttle_end(struct sys_device *sysdev,150struct sysdev_attribute *attr, char *buf)151{152return show_throttle(get_pmd_regs(sysdev), buf, 0);153}154155static ssize_t spu_show_throttle_begin(struct sys_device *sysdev,156struct sysdev_attribute *attr, char *buf)157{158return show_throttle(get_pmd_regs(sysdev), buf, 8);159}160161static ssize_t spu_show_throttle_full_stop(struct sys_device *sysdev,162struct sysdev_attribute *attr, char *buf)163{164return show_throttle(get_pmd_regs(sysdev), buf, 16);165}166167static ssize_t spu_store_throttle_end(struct sys_device *sysdev,168struct sysdev_attribute *attr, const char *buf, size_t size)169{170return store_throttle(get_pmd_regs(sysdev), buf, size, 0);171}172173static ssize_t spu_store_throttle_begin(struct sys_device *sysdev,174struct sysdev_attribute *attr, const char *buf, size_t size)175{176return store_throttle(get_pmd_regs(sysdev), buf, size, 8);177}178179static ssize_t spu_store_throttle_full_stop(struct sys_device *sysdev,180struct sysdev_attribute *attr, const char *buf, size_t size)181{182return store_throttle(get_pmd_regs(sysdev), buf, size, 16);183}184185static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos)186{187struct cbe_pmd_regs __iomem *pmd_regs;188u64 value;189190pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);191value = in_be64(&pmd_regs->ts_ctsr2);192193value = (value >> pos) & 0x3f;194195return sprintf(buf, "%d\n", reg_to_temp(value));196}197198199/* shows the temperature of the DTS on the PPE,200* located near the linear thermal sensor */201static ssize_t ppe_show_temp0(struct sys_device *sysdev,202struct sysdev_attribute *attr, char *buf)203{204return ppe_show_temp(sysdev, buf, 32);205}206207/* shows the temperature of the second DTS on the PPE */208static ssize_t ppe_show_temp1(struct sys_device *sysdev,209struct sysdev_attribute *attr, char *buf)210{211return ppe_show_temp(sysdev, buf, 0);212}213214static ssize_t ppe_show_throttle_end(struct sys_device *sysdev,215struct sysdev_attribute *attr, char *buf)216{217return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 32);218}219220static ssize_t ppe_show_throttle_begin(struct sys_device *sysdev,221struct sysdev_attribute *attr, char *buf)222{223return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 40);224}225226static ssize_t ppe_show_throttle_full_stop(struct sys_device *sysdev,227struct sysdev_attribute *attr, char *buf)228{229return show_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, 48);230}231232static ssize_t ppe_store_throttle_end(struct sys_device *sysdev,233struct sysdev_attribute *attr, const char *buf, size_t size)234{235return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 32);236}237238static ssize_t ppe_store_throttle_begin(struct sys_device *sysdev,239struct sysdev_attribute *attr, const char *buf, size_t size)240{241return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 40);242}243244static ssize_t ppe_store_throttle_full_stop(struct sys_device *sysdev,245struct sysdev_attribute *attr, const char *buf, size_t size)246{247return store_throttle(cbe_get_cpu_pmd_regs(sysdev->id), buf, size, 48);248}249250251static struct sysdev_attribute attr_spu_temperature = {252.attr = {.name = "temperature", .mode = 0400 },253.show = spu_show_temp,254};255256static SYSDEV_PREFIX_ATTR(spu, throttle_end, 0600);257static SYSDEV_PREFIX_ATTR(spu, throttle_begin, 0600);258static SYSDEV_PREFIX_ATTR(spu, throttle_full_stop, 0600);259260261static struct attribute *spu_attributes[] = {262&attr_spu_temperature.attr,263&attr_spu_throttle_end.attr,264&attr_spu_throttle_begin.attr,265&attr_spu_throttle_full_stop.attr,266NULL,267};268269static struct attribute_group spu_attribute_group = {270.name = "thermal",271.attrs = spu_attributes,272};273274static struct sysdev_attribute attr_ppe_temperature0 = {275.attr = {.name = "temperature0", .mode = 0400 },276.show = ppe_show_temp0,277};278279static struct sysdev_attribute attr_ppe_temperature1 = {280.attr = {.name = "temperature1", .mode = 0400 },281.show = ppe_show_temp1,282};283284static SYSDEV_PREFIX_ATTR(ppe, throttle_end, 0600);285static SYSDEV_PREFIX_ATTR(ppe, throttle_begin, 0600);286static SYSDEV_PREFIX_ATTR(ppe, throttle_full_stop, 0600);287288static struct attribute *ppe_attributes[] = {289&attr_ppe_temperature0.attr,290&attr_ppe_temperature1.attr,291&attr_ppe_throttle_end.attr,292&attr_ppe_throttle_begin.attr,293&attr_ppe_throttle_full_stop.attr,294NULL,295};296297static struct attribute_group ppe_attribute_group = {298.name = "thermal",299.attrs = ppe_attributes,300};301302/*303* initialize throttling with default values304*/305static int __init init_default_values(void)306{307int cpu;308struct cbe_pmd_regs __iomem *pmd_regs;309struct sys_device *sysdev;310union ppe_spe_reg tpr;311union spe_reg str1;312u64 str2;313union spe_reg cr1;314u64 cr2;315316/* TPR defaults */317/* ppe318* 1F - no full stop319* 08 - dynamic throttling starts if over 80 degrees320* 03 - dynamic throttling ceases if below 70 degrees */321tpr.ppe = 0x1F0803;322/* spe323* 10 - full stopped when over 96 degrees324* 08 - dynamic throttling starts if over 80 degrees325* 03 - dynamic throttling ceases if below 70 degrees326*/327tpr.spe = 0x100803;328329/* STR defaults */330/* str1331* 10 - stop 16 of 32 cycles332*/333str1.val = 0x1010101010101010ull;334/* str2335* 10 - stop 16 of 32 cycles336*/337str2 = 0x10;338339/* CR defaults */340/* cr1341* 4 - normal operation342*/343cr1.val = 0x0404040404040404ull;344/* cr2345* 4 - normal operation346*/347cr2 = 0x04;348349for_each_possible_cpu (cpu) {350pr_debug("processing cpu %d\n", cpu);351sysdev = get_cpu_sysdev(cpu);352353if (!sysdev) {354pr_info("invalid sysdev pointer for cbe_thermal\n");355return -EINVAL;356}357358pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);359360if (!pmd_regs) {361pr_info("invalid CBE regs pointer for cbe_thermal\n");362return -EINVAL;363}364365out_be64(&pmd_regs->tm_str2, str2);366out_be64(&pmd_regs->tm_str1.val, str1.val);367out_be64(&pmd_regs->tm_tpr.val, tpr.val);368out_be64(&pmd_regs->tm_cr1.val, cr1.val);369out_be64(&pmd_regs->tm_cr2, cr2);370}371372return 0;373}374375376static int __init thermal_init(void)377{378int rc = init_default_values();379380if (rc == 0) {381spu_add_sysdev_attr_group(&spu_attribute_group);382cpu_add_sysdev_attr_group(&ppe_attribute_group);383}384385return rc;386}387module_init(thermal_init);388389static void __exit thermal_exit(void)390{391spu_remove_sysdev_attr_group(&spu_attribute_group);392cpu_remove_sysdev_attr_group(&ppe_attribute_group);393}394module_exit(thermal_exit);395396MODULE_LICENSE("GPL");397MODULE_AUTHOR("Christian Krafft <[email protected]>");398399400401