/*
* thermal support for the cell processor
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Christian Krafft <krafft@de.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/sysdev.h>
#include <linux/kernel.h>
#include <linux/cpu.h>
#include <asm/spu.h>
#include <asm/io.h>
#include <asm/prom.h>
#include "cbe_regs.h"
#include "spu_priv1_mmio.h"
static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev)
{
struct spu *spu;
spu = container_of(sysdev, struct spu, sysdev);
return cbe_get_pmd_regs(spu_devnode(spu));
}
/* returns the value for a given spu in a given register */
static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg)
{
const unsigned int *id;
union spe_reg value;
struct spu *spu;
/* getting the id from the reg attribute will not work on future device-tree layouts
* in future we should store the id to the spu struct and use it here */
spu = container_of(sysdev, struct spu, sysdev);
id = of_get_property(spu_devnode(spu), "reg", NULL);
value.val = in_be64(®->val);
return value.spe[*id];
}
static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf)
{
int value;
struct cbe_pmd_regs __iomem *pmd_regs;
pmd_regs = get_pmd_regs(sysdev);
value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1);
/* clear all other bits */
value &= 0x3F;
/* temp is stored in steps of 2 degrees */
value *= 2;
/* base temp is 65 degrees */
value += 65;
return sprintf(buf, "%d\n", (int) value);
}
static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos)
{
struct cbe_pmd_regs __iomem *pmd_regs;
u64 value;
pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);
value = in_be64(&pmd_regs->ts_ctsr2);
/* access the corresponding byte */
value >>= pos;
/* clear all other bits */
value &= 0x3F;
/* temp is stored in steps of 2 degrees */
value *= 2;
/* base temp is 65 degrees */
value += 65;
return sprintf(buf, "%d\n", (int) value);
}
/* shows the temperature of the DTS on the PPE,
* located near the linear thermal sensor */
static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf)
{
return ppe_show_temp(sysdev, buf, 32);
}
/* shows the temperature of the second DTS on the PPE */
static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf)
{
return ppe_show_temp(sysdev, buf, 0);
}
static struct sysdev_attribute attr_spu_temperature = {
.attr = {.name = "temperature", .mode = 0400 },
.show = spu_show_temp,
};
static struct attribute *spu_attributes[] = {
&attr_spu_temperature.attr,
NULL,
};
static struct attribute_group spu_attribute_group = {
.name = "thermal",
.attrs = spu_attributes,
};
static struct sysdev_attribute attr_ppe_temperature0 = {
.attr = {.name = "temperature0", .mode = 0400 },
.show = ppe_show_temp0,
};
static struct sysdev_attribute attr_ppe_temperature1 = {
.attr = {.name = "temperature1", .mode = 0400 },
.show = ppe_show_temp1,
};
static struct attribute *ppe_attributes[] = {
&attr_ppe_temperature0.attr,
&attr_ppe_temperature1.attr,
NULL,
};
static struct attribute_group ppe_attribute_group = {
.name = "thermal",
.attrs = ppe_attributes,
};
/*
* initialize throttling with default values
*/
static void __init init_default_values(void)
{
int cpu;
struct cbe_pmd_regs __iomem *pmd_regs;
struct sys_device *sysdev;
union ppe_spe_reg tpr;
union spe_reg str1;
u64 str2;
union spe_reg cr1;
u64 cr2;
/* TPR defaults */
/* ppe
* 1F - no full stop
* 08 - dynamic throttling starts if over 80 degrees
* 03 - dynamic throttling ceases if below 70 degrees */
tpr.ppe = 0x1F0803;
/* spe
* 10 - full stopped when over 96 degrees
* 08 - dynamic throttling starts if over 80 degrees
* 03 - dynamic throttling ceases if below 70 degrees
*/
tpr.spe = 0x100803;
/* STR defaults */
/* str1
* 10 - stop 16 of 32 cycles
*/
str1.val = 0x1010101010101010ull;
/* str2
* 10 - stop 16 of 32 cycles
*/
str2 = 0x10;
/* CR defaults */
/* cr1
* 4 - normal operation
*/
cr1.val = 0x0404040404040404ull;
/* cr2
* 4 - normal operation
*/
cr2 = 0x04;
for_each_possible_cpu (cpu) {
pr_debug("processing cpu %d\n", cpu);
sysdev = get_cpu_sysdev(cpu);
pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id);
out_be64(&pmd_regs->tm_str2, str2);
out_be64(&pmd_regs->tm_str1.val, str1.val);
out_be64(&pmd_regs->tm_tpr.val, tpr.val);
out_be64(&pmd_regs->tm_cr1.val, cr1.val);
out_be64(&pmd_regs->tm_cr2, cr2);
}
}
static int __init thermal_init(void)
{
init_default_values();
spu_add_sysdev_attr_group(&spu_attribute_group);
cpu_add_sysdev_attr_group(&ppe_attribute_group);
return 0;
}
module_init(thermal_init);
static void __exit thermal_exit(void)
{
spu_remove_sysdev_attr_group(&spu_attribute_group);
cpu_remove_sysdev_attr_group(&ppe_attribute_group);
}
module_exit(thermal_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");