2005-11-07 08:08:17 +03:00
/*
* Windfarm PowerMac thermal control . iMac G5
*
* ( c ) Copyright 2005 Benjamin Herrenschmidt , IBM Corp .
* < benh @ kernel . crashing . org >
*
* Released under the term of the GNU GPL v2 .
*
* The algorithm used is the PID control algorithm , used the same
* way the published Darwin code does , using the same values that
* are present in the Darwin 8.2 snapshot property lists ( note however
* that none of the code has been re - used , it ' s a complete re - implementation
*
* The various control loops found in Darwin config file are :
*
* PowerMac8 , 1 and PowerMac8 , 2
* = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* System Fans control loop . Different based on models . In addition to the
* usual PID algorithm , the control loop gets 2 additional pairs of linear
* scaling factors ( scale / offsets ) expressed as 4.12 fixed point values
* signed offset , unsigned scale )
*
* The targets are modified such as :
* - the linked control ( second control ) gets the target value as - is
* ( typically the drive fan )
* - the main control ( first control ) gets the target value scaled with
* the first pair of factors , and is then modified as below
2006-01-10 02:10:13 +03:00
* - the value of the target of the CPU Fan control loop is retrieved ,
2005-11-07 08:08:17 +03:00
* scaled with the second pair of factors , and the max of that and
* the scaled target is applied to the main control .
*
* # model_id : 2
* controls : system - fan , drive - bay - fan
* sensors : hd - temp
* PID params : G_d = 0x15400000
* G_p = 0x00200000
* G_r = 0x000002fd
* History = 2 entries
* Input target = 0x3a0000
* Interval = 5 s
* linear - factors : offset = 0xff38 scale = 0x0ccd
* offset = 0x0208 scale = 0x07ae
*
* # model_id : 3
* controls : system - fan , drive - bay - fan
* sensors : hd - temp
* PID params : G_d = 0x08e00000
* G_p = 0x00566666
* G_r = 0x0000072b
* History = 2 entries
* Input target = 0x350000
* Interval = 5 s
* linear - factors : offset = 0xff38 scale = 0x0ccd
* offset = 0x0000 scale = 0x0000
*
* # model_id : 5
* controls : system - fan
* sensors : hd - temp
* PID params : G_d = 0x15400000
* G_p = 0x00233333
* G_r = 0x000002fd
* History = 2 entries
* Input target = 0x3a0000
* Interval = 5 s
* linear - factors : offset = 0x0000 scale = 0x1000
* offset = 0x0091 scale = 0x0bae
*
* CPU Fan control loop . The loop is identical for all models . it
* has an additional pair of scaling factor . This is used to scale the
* systems fan control loop target result ( the one before it gets scaled
* by the System Fans control loop itself ) . Then , the max value of the
* calculated target value and system fan value is sent to the fans
*
* controls : cpu - fan
* sensors : cpu - temp cpu - power
* PID params : From SMU sdb partition
* linear - factors : offset = 0xfb50 scale = 0x1000
*
* CPU Slew control loop . Not implemented . The cpufreq driver in linux is
* completely separate for now , though we could find a way to link it , either
* as a client reacting to overtemp notifications , or directling monitoring
* the CPU temperature
*
* WARNING ! The CPU control loop requires the CPU tmax for the current
* operating point . However , we currently are completely separated from
* the cpufreq driver and thus do not know what the current operating
* point is . Fortunately , we also do not have any hardware supporting anything
* but operating point 0 at the moment , thus we just peek that value directly
* from the SDB partition . If we ever end up with actually slewing the system
* clock and thus changing operating points , we ' ll have to find a way to
* communicate with the CPU freq driver ;
*
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/wait.h>
# include <linux/kmod.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <asm/prom.h>
# include <asm/machdep.h>
# include <asm/io.h>
# include <asm/sections.h>
# include <asm/smu.h>
# include "windfarm.h"
# include "windfarm_pid.h"
# define VERSION "0.4"
# undef DEBUG
# ifdef DEBUG
# define DBG(args...) printk(args)
# else
# define DBG(args...) do { } while(0)
# endif
/* define this to force CPU overtemp to 74 degree, useful for testing
* the overtemp code
*/
# undef HACKED_OVERTEMP
static int wf_smu_mach_model ; /* machine model id */
/* Controls & sensors */
static struct wf_sensor * sensor_cpu_power ;
static struct wf_sensor * sensor_cpu_temp ;
static struct wf_sensor * sensor_hd_temp ;
static struct wf_control * fan_cpu_main ;
static struct wf_control * fan_hd ;
static struct wf_control * fan_system ;
static struct wf_control * cpufreq_clamp ;
/* Set to kick the control loop into life */
static int wf_smu_all_controls_ok , wf_smu_all_sensors_ok , wf_smu_started ;
/* Failure handling.. could be nicer */
# define FAILURE_FAN 0x01
# define FAILURE_SENSOR 0x02
# define FAILURE_OVERTEMP 0x04
static unsigned int wf_smu_failure_state ;
static int wf_smu_readjust , wf_smu_skipping ;
2013-06-30 23:00:42 +04:00
static bool wf_smu_overtemp ;
2005-11-07 08:08:17 +03:00
/*
* * * * * * * System Fans Control Loop * * * * * *
*
*/
/* Parameters for the System Fans control loop. Parameters
* not in this table such as interval , history size , . . .
* are common to all versions and thus hard coded for now .
*/
struct wf_smu_sys_fans_param {
int model_id ;
s32 itarget ;
s32 gd , gp , gr ;
s16 offset0 ;
u16 scale0 ;
s16 offset1 ;
u16 scale1 ;
} ;
# define WF_SMU_SYS_FANS_INTERVAL 5
# define WF_SMU_SYS_FANS_HISTORY_SIZE 2
/* State data used by the system fans control loop
*/
struct wf_smu_sys_fans_state {
int ticks ;
s32 sys_setpoint ;
s32 hd_setpoint ;
s16 offset0 ;
u16 scale0 ;
s16 offset1 ;
u16 scale1 ;
struct wf_pid_state pid ;
} ;
/*
2010-03-16 13:47:56 +03:00
* Configs for SMU System Fan control loop
2005-11-07 08:08:17 +03:00
*/
static struct wf_smu_sys_fans_param wf_smu_sys_all_params [ ] = {
/* Model ID 2 */
{
. model_id = 2 ,
. itarget = 0x3a0000 ,
. gd = 0x15400000 ,
. gp = 0x00200000 ,
. gr = 0x000002fd ,
. offset0 = 0xff38 ,
. scale0 = 0x0ccd ,
. offset1 = 0x0208 ,
. scale1 = 0x07ae ,
} ,
/* Model ID 3 */
{
2005-12-08 08:56:17 +03:00
. model_id = 3 ,
2005-11-07 08:08:17 +03:00
. itarget = 0x350000 ,
. gd = 0x08e00000 ,
. gp = 0x00566666 ,
. gr = 0x0000072b ,
. offset0 = 0xff38 ,
. scale0 = 0x0ccd ,
. offset1 = 0x0000 ,
. scale1 = 0x0000 ,
} ,
/* Model ID 5 */
{
2005-12-08 08:56:17 +03:00
. model_id = 5 ,
2005-11-07 08:08:17 +03:00
. itarget = 0x3a0000 ,
. gd = 0x15400000 ,
. gp = 0x00233333 ,
. gr = 0x000002fd ,
. offset0 = 0x0000 ,
. scale0 = 0x1000 ,
. offset1 = 0x0091 ,
. scale1 = 0x0bae ,
} ,
} ;
# define WF_SMU_SYS_FANS_NUM_CONFIGS ARRAY_SIZE(wf_smu_sys_all_params)
static struct wf_smu_sys_fans_state * wf_smu_sys_fans ;
/*
* * * * * * * CPU Fans Control Loop * * * * * *
*
*/
# define WF_SMU_CPU_FANS_INTERVAL 1
# define WF_SMU_CPU_FANS_MAX_HISTORY 16
# define WF_SMU_CPU_FANS_SIBLING_SCALE 0x00001000
# define WF_SMU_CPU_FANS_SIBLING_OFFSET 0xfffffb50
/* State data used by the cpu fans control loop
*/
struct wf_smu_cpu_fans_state {
int ticks ;
s32 cpu_setpoint ;
s32 scale ;
s32 offset ;
struct wf_cpu_pid_state pid ;
} ;
static struct wf_smu_cpu_fans_state * wf_smu_cpu_fans ;
/*
* * * * * * Implementation * * * * *
*
*/
static void wf_smu_create_sys_fans ( void )
{
struct wf_smu_sys_fans_param * param = NULL ;
struct wf_pid_param pid_param ;
int i ;
/* First, locate the params for this model */
for ( i = 0 ; i < WF_SMU_SYS_FANS_NUM_CONFIGS ; i + + )
if ( wf_smu_sys_all_params [ i ] . model_id = = wf_smu_mach_model ) {
param = & wf_smu_sys_all_params [ i ] ;
break ;
}
/* No params found, put fans to max */
if ( param = = NULL ) {
printk ( KERN_WARNING " windfarm: System fan config not found "
" for this machine model, max fan speed \n " ) ;
goto fail ;
}
/* Alloc & initialize state */
wf_smu_sys_fans = kmalloc ( sizeof ( struct wf_smu_sys_fans_state ) ,
GFP_KERNEL ) ;
if ( wf_smu_sys_fans = = NULL ) {
printk ( KERN_WARNING " windfarm: Memory allocation error "
" max fan speed \n " ) ;
goto fail ;
}
wf_smu_sys_fans - > ticks = 1 ;
wf_smu_sys_fans - > scale0 = param - > scale0 ;
wf_smu_sys_fans - > offset0 = param - > offset0 ;
wf_smu_sys_fans - > scale1 = param - > scale1 ;
wf_smu_sys_fans - > offset1 = param - > offset1 ;
/* Fill PID params */
pid_param . gd = param - > gd ;
pid_param . gp = param - > gp ;
pid_param . gr = param - > gr ;
pid_param . interval = WF_SMU_SYS_FANS_INTERVAL ;
pid_param . history_len = WF_SMU_SYS_FANS_HISTORY_SIZE ;
pid_param . itarget = param - > itarget ;
2012-04-19 02:16:51 +04:00
pid_param . min = wf_control_get_min ( fan_system ) ;
pid_param . max = wf_control_get_max ( fan_system ) ;
2005-11-07 08:08:17 +03:00
if ( fan_hd ) {
pid_param . min =
2012-04-19 02:16:51 +04:00
max ( pid_param . min , wf_control_get_min ( fan_hd ) ) ;
2005-11-07 08:08:17 +03:00
pid_param . max =
2012-04-19 02:16:51 +04:00
min ( pid_param . max , wf_control_get_max ( fan_hd ) ) ;
2005-11-07 08:08:17 +03:00
}
wf_pid_init ( & wf_smu_sys_fans - > pid , & pid_param ) ;
DBG ( " wf: System Fan control initialized. \n " ) ;
DBG ( " itarged=%d.%03d, min=%d RPM, max=%d RPM \n " ,
FIX32TOPRINT ( pid_param . itarget ) , pid_param . min , pid_param . max ) ;
return ;
fail :
if ( fan_system )
wf_control_set_max ( fan_system ) ;
if ( fan_hd )
wf_control_set_max ( fan_hd ) ;
}
static void wf_smu_sys_fans_tick ( struct wf_smu_sys_fans_state * st )
{
s32 new_setpoint , temp , scaled , cputarget ;
int rc ;
if ( - - st - > ticks ! = 0 ) {
if ( wf_smu_readjust )
goto readjust ;
return ;
}
st - > ticks = WF_SMU_SYS_FANS_INTERVAL ;
2012-04-19 02:16:51 +04:00
rc = wf_sensor_get ( sensor_hd_temp , & temp ) ;
2005-11-07 08:08:17 +03:00
if ( rc ) {
printk ( KERN_WARNING " windfarm: HD temp sensor error %d \n " ,
rc ) ;
wf_smu_failure_state | = FAILURE_SENSOR ;
return ;
}
DBG ( " wf_smu: System Fans tick ! HD temp: %d.%03d \n " ,
FIX32TOPRINT ( temp ) ) ;
if ( temp > ( st - > pid . param . itarget + 0x50000 ) )
wf_smu_failure_state | = FAILURE_OVERTEMP ;
new_setpoint = wf_pid_run ( & st - > pid , temp ) ;
DBG ( " wf_smu: new_setpoint: %d RPM \n " , ( int ) new_setpoint ) ;
scaled = ( ( ( ( s64 ) new_setpoint ) * ( s64 ) st - > scale0 ) > > 12 ) + st - > offset0 ;
DBG ( " wf_smu: scaled setpoint: %d RPM \n " , ( int ) scaled ) ;
cputarget = wf_smu_cpu_fans ? wf_smu_cpu_fans - > pid . target : 0 ;
cputarget = ( ( ( ( s64 ) cputarget ) * ( s64 ) st - > scale1 ) > > 12 ) + st - > offset1 ;
scaled = max ( scaled , cputarget ) ;
scaled = max ( scaled , st - > pid . param . min ) ;
scaled = min ( scaled , st - > pid . param . max ) ;
DBG ( " wf_smu: adjusted setpoint: %d RPM \n " , ( int ) scaled ) ;
if ( st - > sys_setpoint = = scaled & & new_setpoint = = st - > hd_setpoint )
return ;
st - > sys_setpoint = scaled ;
st - > hd_setpoint = new_setpoint ;
readjust :
if ( fan_system & & wf_smu_failure_state = = 0 ) {
2012-04-19 02:16:51 +04:00
rc = wf_control_set ( fan_system , st - > sys_setpoint ) ;
2005-11-07 08:08:17 +03:00
if ( rc ) {
printk ( KERN_WARNING " windfarm: Sys fan error %d \n " ,
rc ) ;
wf_smu_failure_state | = FAILURE_FAN ;
}
}
if ( fan_hd & & wf_smu_failure_state = = 0 ) {
2012-04-19 02:16:51 +04:00
rc = wf_control_set ( fan_hd , st - > hd_setpoint ) ;
2005-11-07 08:08:17 +03:00
if ( rc ) {
printk ( KERN_WARNING " windfarm: HD fan error %d \n " ,
rc ) ;
wf_smu_failure_state | = FAILURE_FAN ;
}
}
}
static void wf_smu_create_cpu_fans ( void )
{
struct wf_cpu_pid_param pid_param ;
2006-07-12 09:40:29 +04:00
const struct smu_sdbp_header * hdr ;
2005-11-07 08:08:17 +03:00
struct smu_sdbp_cpupiddata * piddata ;
struct smu_sdbp_fvt * fvt ;
s32 tmax , tdelta , maxpow , powadj ;
/* First, locate the PID params in SMU SBD */
hdr = smu_get_sdb_partition ( SMU_SDB_CPUPIDDATA_ID , NULL ) ;
if ( hdr = = 0 ) {
printk ( KERN_WARNING " windfarm: CPU PID fan config not found "
" max fan speed \n " ) ;
goto fail ;
}
piddata = ( struct smu_sdbp_cpupiddata * ) & hdr [ 1 ] ;
/* Get the FVT params for operating point 0 (the only supported one
* for now ) in order to get tmax
*/
hdr = smu_get_sdb_partition ( SMU_SDB_FVT_ID , NULL ) ;
if ( hdr ) {
fvt = ( struct smu_sdbp_fvt * ) & hdr [ 1 ] ;
tmax = ( ( s32 ) fvt - > maxtemp ) < < 16 ;
} else
tmax = 0x5e0000 ; /* 94 degree default */
/* Alloc & initialize state */
wf_smu_cpu_fans = kmalloc ( sizeof ( struct wf_smu_cpu_fans_state ) ,
GFP_KERNEL ) ;
if ( wf_smu_cpu_fans = = NULL )
goto fail ;
wf_smu_cpu_fans - > ticks = 1 ;
wf_smu_cpu_fans - > scale = WF_SMU_CPU_FANS_SIBLING_SCALE ;
wf_smu_cpu_fans - > offset = WF_SMU_CPU_FANS_SIBLING_OFFSET ;
/* Fill PID params */
pid_param . interval = WF_SMU_CPU_FANS_INTERVAL ;
pid_param . history_len = piddata - > history_len ;
if ( pid_param . history_len > WF_CPU_PID_MAX_HISTORY ) {
printk ( KERN_WARNING " windfarm: History size overflow on "
" CPU control loop (%d) \n " , piddata - > history_len ) ;
pid_param . history_len = WF_CPU_PID_MAX_HISTORY ;
}
pid_param . gd = piddata - > gd ;
pid_param . gp = piddata - > gp ;
pid_param . gr = piddata - > gr / pid_param . history_len ;
tdelta = ( ( s32 ) piddata - > target_temp_delta ) < < 16 ;
maxpow = ( ( s32 ) piddata - > max_power ) < < 16 ;
powadj = ( ( s32 ) piddata - > power_adj ) < < 16 ;
pid_param . tmax = tmax ;
pid_param . ttarget = tmax - tdelta ;
pid_param . pmaxadj = maxpow - powadj ;
2012-04-19 02:16:51 +04:00
pid_param . min = wf_control_get_min ( fan_cpu_main ) ;
pid_param . max = wf_control_get_max ( fan_cpu_main ) ;
2005-11-07 08:08:17 +03:00
wf_cpu_pid_init ( & wf_smu_cpu_fans - > pid , & pid_param ) ;
DBG ( " wf: CPU Fan control initialized. \n " ) ;
DBG ( " ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM \n " ,
FIX32TOPRINT ( pid_param . ttarget ) , FIX32TOPRINT ( pid_param . tmax ) ,
pid_param . min , pid_param . max ) ;
return ;
fail :
printk ( KERN_WARNING " windfarm: CPU fan config not found \n "
" for this machine model, max fan speed \n " ) ;
if ( cpufreq_clamp )
wf_control_set_max ( cpufreq_clamp ) ;
if ( fan_cpu_main )
wf_control_set_max ( fan_cpu_main ) ;
}
static void wf_smu_cpu_fans_tick ( struct wf_smu_cpu_fans_state * st )
{
s32 new_setpoint , temp , power , systarget ;
int rc ;
if ( - - st - > ticks ! = 0 ) {
if ( wf_smu_readjust )
goto readjust ;
return ;
}
st - > ticks = WF_SMU_CPU_FANS_INTERVAL ;
2012-04-19 02:16:51 +04:00
rc = wf_sensor_get ( sensor_cpu_temp , & temp ) ;
2005-11-07 08:08:17 +03:00
if ( rc ) {
printk ( KERN_WARNING " windfarm: CPU temp sensor error %d \n " ,
rc ) ;
wf_smu_failure_state | = FAILURE_SENSOR ;
return ;
}
2012-04-19 02:16:51 +04:00
rc = wf_sensor_get ( sensor_cpu_power , & power ) ;
2005-11-07 08:08:17 +03:00
if ( rc ) {
printk ( KERN_WARNING " windfarm: CPU power sensor error %d \n " ,
rc ) ;
wf_smu_failure_state | = FAILURE_SENSOR ;
return ;
}
DBG ( " wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d \n " ,
FIX32TOPRINT ( temp ) , FIX32TOPRINT ( power ) ) ;
# ifdef HACKED_OVERTEMP
if ( temp > 0x4a0000 )
wf_smu_failure_state | = FAILURE_OVERTEMP ;
# else
if ( temp > st - > pid . param . tmax )
wf_smu_failure_state | = FAILURE_OVERTEMP ;
# endif
new_setpoint = wf_cpu_pid_run ( & st - > pid , power , temp ) ;
DBG ( " wf_smu: new_setpoint: %d RPM \n " , ( int ) new_setpoint ) ;
systarget = wf_smu_sys_fans ? wf_smu_sys_fans - > pid . target : 0 ;
systarget = ( ( ( ( s64 ) systarget ) * ( s64 ) st - > scale ) > > 12 )
+ st - > offset ;
new_setpoint = max ( new_setpoint , systarget ) ;
new_setpoint = max ( new_setpoint , st - > pid . param . min ) ;
new_setpoint = min ( new_setpoint , st - > pid . param . max ) ;
DBG ( " wf_smu: adjusted setpoint: %d RPM \n " , ( int ) new_setpoint ) ;
if ( st - > cpu_setpoint = = new_setpoint )
return ;
st - > cpu_setpoint = new_setpoint ;
readjust :
if ( fan_cpu_main & & wf_smu_failure_state = = 0 ) {
2012-04-19 02:16:51 +04:00
rc = wf_control_set ( fan_cpu_main , st - > cpu_setpoint ) ;
2005-11-07 08:08:17 +03:00
if ( rc ) {
printk ( KERN_WARNING " windfarm: CPU main fan "
" error %d \n " , rc ) ;
wf_smu_failure_state | = FAILURE_FAN ;
}
}
}
/*
* * * * * * * Setup / Init / Misc . . . * * * * * *
*
*/
static void wf_smu_tick ( void )
{
unsigned int last_failure = wf_smu_failure_state ;
unsigned int new_failure ;
if ( ! wf_smu_started ) {
DBG ( " wf: creating control loops ! \n " ) ;
wf_smu_create_sys_fans ( ) ;
wf_smu_create_cpu_fans ( ) ;
wf_smu_started = 1 ;
}
/* Skipping ticks */
if ( wf_smu_skipping & & - - wf_smu_skipping )
return ;
wf_smu_failure_state = 0 ;
if ( wf_smu_sys_fans )
wf_smu_sys_fans_tick ( wf_smu_sys_fans ) ;
if ( wf_smu_cpu_fans )
wf_smu_cpu_fans_tick ( wf_smu_cpu_fans ) ;
wf_smu_readjust = 0 ;
new_failure = wf_smu_failure_state & ~ last_failure ;
/* If entering failure mode, clamp cpufreq and ramp all
* fans to full speed .
*/
if ( wf_smu_failure_state & & ! last_failure ) {
if ( cpufreq_clamp )
wf_control_set_max ( cpufreq_clamp ) ;
if ( fan_system )
wf_control_set_max ( fan_system ) ;
if ( fan_cpu_main )
wf_control_set_max ( fan_cpu_main ) ;
if ( fan_hd )
wf_control_set_max ( fan_hd ) ;
}
/* If leaving failure mode, unclamp cpufreq and readjust
* all fans on next iteration
*/
if ( ! wf_smu_failure_state & & last_failure ) {
if ( cpufreq_clamp )
wf_control_set_min ( cpufreq_clamp ) ;
wf_smu_readjust = 1 ;
}
/* Overtemp condition detected, notify and start skipping a couple
* ticks to let the temperature go down
*/
if ( new_failure & FAILURE_OVERTEMP ) {
wf_set_overtemp ( ) ;
wf_smu_skipping = 2 ;
2013-06-30 23:00:42 +04:00
wf_smu_overtemp = true ;
2005-11-07 08:08:17 +03:00
}
/* We only clear the overtemp condition if overtemp is cleared
* _and_ no other failure is present . Since a sensor error will
* clear the overtemp condition ( can ' t measure temperature ) at
* the control loop levels , but we don ' t want to keep it clear
* here in this case
*/
2013-06-30 23:00:42 +04:00
if ( ! wf_smu_failure_state & & wf_smu_overtemp ) {
2005-11-07 08:08:17 +03:00
wf_clear_overtemp ( ) ;
2013-06-30 23:00:42 +04:00
wf_smu_overtemp = false ;
}
2005-11-07 08:08:17 +03:00
}
static void wf_smu_new_control ( struct wf_control * ct )
{
if ( wf_smu_all_controls_ok )
return ;
if ( fan_cpu_main = = NULL & & ! strcmp ( ct - > name , " cpu-fan " ) ) {
2006-02-08 08:42:51 +03:00
if ( wf_get_control ( ct ) = = 0 )
2005-11-07 08:08:17 +03:00
fan_cpu_main = ct ;
}
if ( fan_system = = NULL & & ! strcmp ( ct - > name , " system-fan " ) ) {
2006-02-08 08:42:51 +03:00
if ( wf_get_control ( ct ) = = 0 )
2005-11-07 08:08:17 +03:00
fan_system = ct ;
}
if ( cpufreq_clamp = = NULL & & ! strcmp ( ct - > name , " cpufreq-clamp " ) ) {
if ( wf_get_control ( ct ) = = 0 )
cpufreq_clamp = ct ;
}
/* Darwin property list says the HD fan is only for model ID
* 0 , 1 , 2 and 3
*/
if ( wf_smu_mach_model > 3 ) {
if ( fan_system & & fan_cpu_main & & cpufreq_clamp )
wf_smu_all_controls_ok = 1 ;
return ;
}
if ( fan_hd = = NULL & & ! strcmp ( ct - > name , " drive-bay-fan " ) ) {
2006-02-08 08:42:51 +03:00
if ( wf_get_control ( ct ) = = 0 )
2005-11-07 08:08:17 +03:00
fan_hd = ct ;
}
if ( fan_system & & fan_hd & & fan_cpu_main & & cpufreq_clamp )
wf_smu_all_controls_ok = 1 ;
}
static void wf_smu_new_sensor ( struct wf_sensor * sr )
{
if ( wf_smu_all_sensors_ok )
return ;
if ( sensor_cpu_power = = NULL & & ! strcmp ( sr - > name , " cpu-power " ) ) {
2006-02-08 08:42:51 +03:00
if ( wf_get_sensor ( sr ) = = 0 )
2005-11-07 08:08:17 +03:00
sensor_cpu_power = sr ;
}
if ( sensor_cpu_temp = = NULL & & ! strcmp ( sr - > name , " cpu-temp " ) ) {
2006-02-08 08:42:51 +03:00
if ( wf_get_sensor ( sr ) = = 0 )
2005-11-07 08:08:17 +03:00
sensor_cpu_temp = sr ;
}
if ( sensor_hd_temp = = NULL & & ! strcmp ( sr - > name , " hd-temp " ) ) {
2006-02-08 08:42:51 +03:00
if ( wf_get_sensor ( sr ) = = 0 )
2005-11-07 08:08:17 +03:00
sensor_hd_temp = sr ;
}
if ( sensor_cpu_power & & sensor_cpu_temp & & sensor_hd_temp )
wf_smu_all_sensors_ok = 1 ;
}
static int wf_smu_notify ( struct notifier_block * self ,
unsigned long event , void * data )
{
switch ( event ) {
case WF_EVENT_NEW_CONTROL :
DBG ( " wf: new control %s detected \n " ,
( ( struct wf_control * ) data ) - > name ) ;
wf_smu_new_control ( data ) ;
wf_smu_readjust = 1 ;
break ;
case WF_EVENT_NEW_SENSOR :
DBG ( " wf: new sensor %s detected \n " ,
( ( struct wf_sensor * ) data ) - > name ) ;
wf_smu_new_sensor ( data ) ;
break ;
case WF_EVENT_TICK :
if ( wf_smu_all_controls_ok & & wf_smu_all_sensors_ok )
wf_smu_tick ( ) ;
}
return 0 ;
}
static struct notifier_block wf_smu_events = {
. notifier_call = wf_smu_notify ,
} ;
static int wf_init_pm ( void )
{
2006-07-12 09:40:29 +04:00
const struct smu_sdbp_header * hdr ;
2005-11-07 08:08:17 +03:00
hdr = smu_get_sdb_partition ( SMU_SDB_SENSORTREE_ID , NULL ) ;
if ( hdr ! = 0 ) {
struct smu_sdbp_sensortree * st =
( struct smu_sdbp_sensortree * ) & hdr [ 1 ] ;
wf_smu_mach_model = st - > model_id ;
}
printk ( KERN_INFO " windfarm: Initializing for iMacG5 model ID %d \n " ,
wf_smu_mach_model ) ;
return 0 ;
}
2006-10-10 05:47:31 +04:00
static int wf_smu_probe ( struct platform_device * ddev )
2005-11-07 08:08:17 +03:00
{
wf_register_client ( & wf_smu_events ) ;
return 0 ;
}
2012-12-22 03:03:50 +04:00
static int wf_smu_remove ( struct platform_device * ddev )
2005-11-07 08:08:17 +03:00
{
wf_unregister_client ( & wf_smu_events ) ;
/* XXX We don't have yet a guarantee that our callback isn't
* in progress when returning from wf_unregister_client , so
* we add an arbitrary delay . I ' ll have to fix that in the core
*/
msleep ( 1000 ) ;
/* Release all sensors */
/* One more crappy race: I don't think we have any guarantee here
* that the attribute callback won ' t race with the sensor beeing
* disposed of , and I ' m not 100 % certain what best way to deal
* with that except by adding locks all over . . . I ' ll do that
* eventually but heh , who ever rmmod this module anyway ?
*/
2006-02-08 08:42:51 +03:00
if ( sensor_cpu_power )
2005-11-07 08:08:17 +03:00
wf_put_sensor ( sensor_cpu_power ) ;
2006-02-08 08:42:51 +03:00
if ( sensor_cpu_temp )
2005-11-07 08:08:17 +03:00
wf_put_sensor ( sensor_cpu_temp ) ;
2006-02-08 08:42:51 +03:00
if ( sensor_hd_temp )
2005-11-07 08:08:17 +03:00
wf_put_sensor ( sensor_hd_temp ) ;
/* Release all controls */
2006-02-08 08:42:51 +03:00
if ( fan_cpu_main )
2005-11-07 08:08:17 +03:00
wf_put_control ( fan_cpu_main ) ;
2006-02-08 08:42:51 +03:00
if ( fan_hd )
2005-11-07 08:08:17 +03:00
wf_put_control ( fan_hd ) ;
2006-02-08 08:42:51 +03:00
if ( fan_system )
2005-11-07 08:08:17 +03:00
wf_put_control ( fan_system ) ;
if ( cpufreq_clamp )
wf_put_control ( cpufreq_clamp ) ;
/* Destroy control loops state structures */
2010-03-19 02:01:42 +03:00
kfree ( wf_smu_sys_fans ) ;
kfree ( wf_smu_cpu_fans ) ;
2005-11-07 08:08:17 +03:00
return 0 ;
}
2006-10-10 05:47:31 +04:00
static struct platform_driver wf_smu_driver = {
2005-11-07 08:08:17 +03:00
. probe = wf_smu_probe ,
2012-12-22 03:03:50 +04:00
. remove = wf_smu_remove ,
2006-10-10 05:47:31 +04:00
. driver = {
. name = " windfarm " ,
} ,
2005-11-07 08:08:17 +03:00
} ;
static int __init wf_smu_init ( void )
{
int rc = - ENODEV ;
2010-02-02 07:34:14 +03:00
if ( of_machine_is_compatible ( " PowerMac8,1 " ) | |
of_machine_is_compatible ( " PowerMac8,2 " ) )
2005-11-07 08:08:17 +03:00
rc = wf_init_pm ( ) ;
if ( rc = = 0 ) {
# ifdef MODULE
request_module ( " windfarm_smu_controls " ) ;
request_module ( " windfarm_smu_sensors " ) ;
request_module ( " windfarm_lm75_sensor " ) ;
2006-11-16 06:00:57 +03:00
request_module ( " windfarm_cpufreq_clamp " ) ;
2005-11-07 08:08:17 +03:00
# endif /* MODULE */
2006-10-10 05:47:31 +04:00
platform_driver_register ( & wf_smu_driver ) ;
2005-11-07 08:08:17 +03:00
}
return rc ;
}
static void __exit wf_smu_exit ( void )
{
2006-10-10 05:47:31 +04:00
platform_driver_unregister ( & wf_smu_driver ) ;
2005-11-07 08:08:17 +03:00
}
module_init ( wf_smu_init ) ;
module_exit ( wf_smu_exit ) ;
MODULE_AUTHOR ( " Benjamin Herrenschmidt <benh@kernel.crashing.org> " ) ;
MODULE_DESCRIPTION ( " Thermal control logic for iMac G5 " ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-04-19 00:33:53 +04:00
MODULE_ALIAS ( " platform:windfarm " ) ;