2006-10-24 18:31:25 +02:00
/*
* thermal support for the cell processor
*
2007-04-23 21:35:41 +02:00
* This module adds some sysfs attributes to cpu and spu nodes .
* Base for measurements are the digital thermal sensors ( DTS )
* located on the chip .
* The accuracy is 2 degrees , starting from 65 up to 125 degrees celsius
* The attributes can be found under
* / sys / devices / system / cpu / cpuX / thermal
* / sys / devices / system / spu / spuX / thermal
*
* The following attributes are added for each node :
* temperature :
* contains the current temperature measured by the DTS
* throttle_begin :
* throttling begins when temperature is greater or equal to
* throttle_begin . Setting this value to 125 prevents throttling .
* throttle_end :
* throttling is being ceased , if the temperature is lower than
* throttle_end . Due to a delay between applying throttling and
* a reduced temperature this value should be less than throttle_begin .
* A value equal to throttle_begin provides only a very little hysteresis .
* throttle_full_stop :
* If the temperatrue is greater or equal to throttle_full_stop ,
* full throttling is applied to the cpu or spu . This value should be
* greater than throttle_begin and throttle_end . Setting this value to
* 65 prevents the unit from running code at all .
*
2006-10-24 18:31:25 +02:00
* ( 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 0213 9 , 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>
2007-10-04 15:40:42 +10:00
# include <asm/cell-regs.h>
2006-10-24 18:31:25 +02:00
2006-11-23 00:46:49 +01:00
# include "spu_priv1_mmio.h"
2006-10-24 18:31:25 +02:00
2007-04-23 21:35:41 +02:00
# define TEMP_MIN 65
# define TEMP_MAX 125
# define SYSDEV_PREFIX_ATTR(_prefix,_name,_mode) \
struct sysdev_attribute attr_ # # _prefix # # _ # # _name = { \
. attr = { . name = __stringify ( _name ) , . mode = _mode } , \
. show = _prefix # # _show_ # # _name , \
. store = _prefix # # _store_ # # _name , \
} ;
2007-04-23 21:35:40 +02:00
static inline u8 reg_to_temp ( u8 reg_value )
{
2007-04-23 21:35:41 +02:00
return ( ( reg_value & 0x3f ) < < 1 ) + TEMP_MIN ;
}
static inline u8 temp_to_reg ( u8 temp )
{
return ( ( temp - TEMP_MIN ) > > 1 ) & 0x3f ;
2007-04-23 21:35:40 +02:00
}
2006-10-24 18:31:25 +02:00
static struct cbe_pmd_regs __iomem * get_pmd_regs ( struct sys_device * sysdev )
{
struct spu * spu ;
spu = container_of ( sysdev , struct spu , sysdev ) ;
2006-11-23 00:46:49 +01:00
return cbe_get_pmd_regs ( spu_devnode ( spu ) ) ;
2006-10-24 18:31:25 +02:00
}
/* 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 )
{
union spe_reg value ;
struct spu * spu ;
spu = container_of ( sysdev , struct spu , sysdev ) ;
value . val = in_be64 ( & reg - > val ) ;
2007-08-23 03:01:25 +10:00
return value . spe [ spu - > spe_id ] ;
2006-10-24 18:31:25 +02:00
}
2008-07-01 18:48:41 +02:00
static ssize_t spu_show_temp ( struct sys_device * sysdev , struct sysdev_attribute * attr ,
char * buf )
2006-10-24 18:31:25 +02:00
{
2007-04-23 21:35:40 +02:00
u8 value ;
2006-10-24 18:31:25 +02:00
struct cbe_pmd_regs __iomem * pmd_regs ;
pmd_regs = get_pmd_regs ( sysdev ) ;
value = spu_read_register_value ( sysdev , & pmd_regs - > ts_ctsr1 ) ;
2007-04-23 21:35:40 +02:00
return sprintf ( buf , " %d \n " , reg_to_temp ( value ) ) ;
2006-10-24 18:31:25 +02:00
}
2007-04-23 21:35:41 +02:00
static ssize_t show_throttle ( struct cbe_pmd_regs __iomem * pmd_regs , char * buf , int pos )
{
u64 value ;
value = in_be64 ( & pmd_regs - > tm_tpr . val ) ;
/* access the corresponding byte */
value > > = pos ;
value & = 0x3F ;
return sprintf ( buf , " %d \n " , reg_to_temp ( value ) ) ;
}
static ssize_t store_throttle ( struct cbe_pmd_regs __iomem * pmd_regs , const char * buf , size_t size , int pos )
{
u64 reg_value ;
int temp ;
u64 new_value ;
int ret ;
ret = sscanf ( buf , " %u " , & temp ) ;
if ( ret ! = 1 | | temp < TEMP_MIN | | temp > TEMP_MAX )
return - EINVAL ;
new_value = temp_to_reg ( temp ) ;
reg_value = in_be64 ( & pmd_regs - > tm_tpr . val ) ;
/* zero out bits for new value */
reg_value & = ~ ( 0xffull < < pos ) ;
/* set bits to new value */
reg_value | = new_value < < pos ;
out_be64 ( & pmd_regs - > tm_tpr . val , reg_value ) ;
return size ;
}
2008-07-01 18:48:41 +02:00
static ssize_t spu_show_throttle_end ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2007-04-23 21:35:41 +02:00
{
return show_throttle ( get_pmd_regs ( sysdev ) , buf , 0 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t spu_show_throttle_begin ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2007-04-23 21:35:41 +02:00
{
return show_throttle ( get_pmd_regs ( sysdev ) , buf , 8 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t spu_show_throttle_full_stop ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2007-04-23 21:35:41 +02:00
{
return show_throttle ( get_pmd_regs ( sysdev ) , buf , 16 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t spu_store_throttle_end ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , const char * buf , size_t size )
2007-04-23 21:35:41 +02:00
{
return store_throttle ( get_pmd_regs ( sysdev ) , buf , size , 0 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t spu_store_throttle_begin ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , const char * buf , size_t size )
2007-04-23 21:35:41 +02:00
{
return store_throttle ( get_pmd_regs ( sysdev ) , buf , size , 8 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t spu_store_throttle_full_stop ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , const char * buf , size_t size )
2007-04-23 21:35:41 +02:00
{
return store_throttle ( get_pmd_regs ( sysdev ) , buf , size , 16 ) ;
}
2006-10-24 18:31:25 +02:00
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 ) ;
2007-04-23 21:35:40 +02:00
value = ( value > > pos ) & 0x3f ;
2006-10-24 18:31:25 +02:00
2007-04-23 21:35:40 +02:00
return sprintf ( buf , " %d \n " , reg_to_temp ( value ) ) ;
2006-10-24 18:31:25 +02:00
}
/* shows the temperature of the DTS on the PPE,
* located near the linear thermal sensor */
2008-07-01 18:48:41 +02:00
static ssize_t ppe_show_temp0 ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2006-10-24 18:31:25 +02:00
{
return ppe_show_temp ( sysdev , buf , 32 ) ;
}
/* shows the temperature of the second DTS on the PPE */
2008-07-01 18:48:41 +02:00
static ssize_t ppe_show_temp1 ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2006-10-24 18:31:25 +02:00
{
return ppe_show_temp ( sysdev , buf , 0 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t ppe_show_throttle_end ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2007-04-23 21:35:41 +02:00
{
return show_throttle ( cbe_get_cpu_pmd_regs ( sysdev - > id ) , buf , 32 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t ppe_show_throttle_begin ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2007-04-23 21:35:41 +02:00
{
return show_throttle ( cbe_get_cpu_pmd_regs ( sysdev - > id ) , buf , 40 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t ppe_show_throttle_full_stop ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , char * buf )
2007-04-23 21:35:41 +02:00
{
return show_throttle ( cbe_get_cpu_pmd_regs ( sysdev - > id ) , buf , 48 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t ppe_store_throttle_end ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , const char * buf , size_t size )
2007-04-23 21:35:41 +02:00
{
return store_throttle ( cbe_get_cpu_pmd_regs ( sysdev - > id ) , buf , size , 32 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t ppe_store_throttle_begin ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , const char * buf , size_t size )
2007-04-23 21:35:41 +02:00
{
return store_throttle ( cbe_get_cpu_pmd_regs ( sysdev - > id ) , buf , size , 40 ) ;
}
2008-07-01 18:48:41 +02:00
static ssize_t ppe_store_throttle_full_stop ( struct sys_device * sysdev ,
struct sysdev_attribute * attr , const char * buf , size_t size )
2007-04-23 21:35:41 +02:00
{
return store_throttle ( cbe_get_cpu_pmd_regs ( sysdev - > id ) , buf , size , 48 ) ;
}
2006-10-24 18:31:25 +02:00
static struct sysdev_attribute attr_spu_temperature = {
. attr = { . name = " temperature " , . mode = 0400 } ,
. show = spu_show_temp ,
} ;
2007-04-23 21:35:41 +02:00
static SYSDEV_PREFIX_ATTR ( spu , throttle_end , 0600 ) ;
static SYSDEV_PREFIX_ATTR ( spu , throttle_begin , 0600 ) ;
static SYSDEV_PREFIX_ATTR ( spu , throttle_full_stop , 0600 ) ;
2006-10-24 18:31:25 +02:00
static struct attribute * spu_attributes [ ] = {
& attr_spu_temperature . attr ,
2007-04-23 21:35:41 +02:00
& attr_spu_throttle_end . attr ,
& attr_spu_throttle_begin . attr ,
& attr_spu_throttle_full_stop . attr ,
2006-12-07 19:01:16 +01:00
NULL ,
2006-10-24 18:31:25 +02:00
} ;
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 ,
} ;
2007-04-23 21:35:41 +02:00
static SYSDEV_PREFIX_ATTR ( ppe , throttle_end , 0600 ) ;
static SYSDEV_PREFIX_ATTR ( ppe , throttle_begin , 0600 ) ;
static SYSDEV_PREFIX_ATTR ( ppe , throttle_full_stop , 0600 ) ;
2006-10-24 18:31:25 +02:00
static struct attribute * ppe_attributes [ ] = {
& attr_ppe_temperature0 . attr ,
& attr_ppe_temperature1 . attr ,
2007-04-23 21:35:41 +02:00
& attr_ppe_throttle_end . attr ,
& attr_ppe_throttle_begin . attr ,
& attr_ppe_throttle_full_stop . attr ,
2006-12-07 19:01:16 +01:00
NULL ,
2006-10-24 18:31:25 +02:00
} ;
static struct attribute_group ppe_attribute_group = {
. name = " thermal " ,
. attrs = ppe_attributes ,
} ;
/*
* initialize throttling with default values
*/
2007-07-20 21:39:24 +02:00
static int __init init_default_values ( void )
2006-10-24 18:31:25 +02:00
{
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 ) ;
2007-07-20 21:39:24 +02:00
if ( ! sysdev ) {
pr_info ( " invalid sysdev pointer for cbe_thermal \n " ) ;
return - EINVAL ;
}
2006-10-24 18:31:25 +02:00
pmd_regs = cbe_get_cpu_pmd_regs ( sysdev - > id ) ;
2007-07-20 21:39:24 +02:00
if ( ! pmd_regs ) {
pr_info ( " invalid CBE regs pointer for cbe_thermal \n " ) ;
return - EINVAL ;
}
2006-10-24 18:31:25 +02:00
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 ) ;
}
2007-07-20 21:39:24 +02:00
return 0 ;
2006-10-24 18:31:25 +02:00
}
static int __init thermal_init ( void )
{
2007-07-20 21:39:24 +02:00
int rc = init_default_values ( ) ;
2006-10-24 18:31:25 +02:00
2007-07-20 21:39:24 +02:00
if ( rc = = 0 ) {
spu_add_sysdev_attr_group ( & spu_attribute_group ) ;
cpu_add_sysdev_attr_group ( & ppe_attribute_group ) ;
}
2006-10-24 18:31:25 +02:00
2007-07-20 21:39:24 +02:00
return rc ;
2006-10-24 18:31:25 +02:00
}
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> " ) ;