2019-06-03 08:45:03 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-04-29 19:42:27 +04:00
/*
* Windfarm PowerMac thermal control .
* Control loops for PowerMac7 , 2 and 7 , 3
*
* Copyright ( C ) 2012 Benjamin Herrenschmidt , IBM Corp .
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/platform_device.h>
# include <linux/reboot.h>
# include <asm/prom.h>
# include <asm/smu.h>
# include "windfarm.h"
# include "windfarm_pid.h"
# include "windfarm_mpu.h"
# define VERSION "1.0"
# undef DEBUG
# undef LOTSA_DEBUG
# ifdef DEBUG
# define DBG(args...) printk(args)
# else
# define DBG(args...) do { } while(0)
# endif
# ifdef LOTSA_DEBUG
# define DBG_LOTS(args...) printk(args)
# else
# define DBG_LOTS(args...) do { } while(0)
# endif
/* define this to force CPU overtemp to 60 degree, useful for testing
* the overtemp code
*/
# undef HACKED_OVERTEMP
/* We currently only handle 2 chips */
# define NR_CHIPS 2
# define NR_CPU_FANS 3 * NR_CHIPS
/* Controls and sensors */
static struct wf_sensor * sens_cpu_temp [ NR_CHIPS ] ;
static struct wf_sensor * sens_cpu_volts [ NR_CHIPS ] ;
static struct wf_sensor * sens_cpu_amps [ NR_CHIPS ] ;
static struct wf_sensor * backside_temp ;
static struct wf_sensor * drives_temp ;
static struct wf_control * cpu_front_fans [ NR_CHIPS ] ;
static struct wf_control * cpu_rear_fans [ NR_CHIPS ] ;
static struct wf_control * cpu_pumps [ NR_CHIPS ] ;
static struct wf_control * backside_fan ;
static struct wf_control * drives_fan ;
static struct wf_control * slots_fan ;
static struct wf_control * cpufreq_clamp ;
/* We keep a temperature history for average calculation of 180s */
# define CPU_TEMP_HIST_SIZE 180
/* Fixed speed for slot fan */
# define SLOTS_FAN_DEFAULT_PWM 40
/* Scale value for CPU intake fans */
# define CPU_INTAKE_SCALE 0x0000f852
/* PID loop state */
static const struct mpu_data * cpu_mpu_data [ NR_CHIPS ] ;
static struct wf_cpu_pid_state cpu_pid [ NR_CHIPS ] ;
static bool cpu_pid_combined ;
static u32 cpu_thist [ CPU_TEMP_HIST_SIZE ] ;
static int cpu_thist_pt ;
static s64 cpu_thist_total ;
static s32 cpu_all_tmax = 100 < < 16 ;
static struct wf_pid_state backside_pid ;
static int backside_tick ;
static struct wf_pid_state drives_pid ;
static int drives_tick ;
static int nr_chips ;
static bool have_all_controls ;
static bool have_all_sensors ;
static bool started ;
static int failure_state ;
# define FAILURE_SENSOR 1
# define FAILURE_FAN 2
# define FAILURE_PERM 4
# define FAILURE_LOW_OVERTEMP 8
# define FAILURE_HIGH_OVERTEMP 16
/* Overtemp values */
# define LOW_OVER_AVERAGE 0
# define LOW_OVER_IMMEDIATE (10 << 16)
# define LOW_OVER_CLEAR ((-10) << 16)
# define HIGH_OVER_IMMEDIATE (14 << 16)
# define HIGH_OVER_AVERAGE (10 << 16)
# define HIGH_OVER_IMMEDIATE (14 << 16)
static void cpu_max_all_fans ( void )
{
int i ;
/* We max all CPU fans in case of a sensor error. We also do the
* cpufreq clamping now , even if it ' s supposedly done later by the
* generic code anyway , we do it earlier here to react faster
*/
if ( cpufreq_clamp )
wf_control_set_max ( cpufreq_clamp ) ;
for ( i = 0 ; i < nr_chips ; i + + ) {
if ( cpu_front_fans [ i ] )
wf_control_set_max ( cpu_front_fans [ i ] ) ;
if ( cpu_rear_fans [ i ] )
wf_control_set_max ( cpu_rear_fans [ i ] ) ;
if ( cpu_pumps [ i ] )
wf_control_set_max ( cpu_pumps [ i ] ) ;
}
}
static int cpu_check_overtemp ( s32 temp )
{
int new_state = 0 ;
s32 t_avg , t_old ;
static bool first = true ;
/* First check for immediate overtemps */
if ( temp > = ( cpu_all_tmax + LOW_OVER_IMMEDIATE ) ) {
new_state | = FAILURE_LOW_OVERTEMP ;
if ( ( failure_state & FAILURE_LOW_OVERTEMP ) = = 0 )
printk ( KERN_ERR " windfarm: Overtemp due to immediate CPU "
" temperature ! \n " ) ;
}
if ( temp > = ( cpu_all_tmax + HIGH_OVER_IMMEDIATE ) ) {
new_state | = FAILURE_HIGH_OVERTEMP ;
if ( ( failure_state & FAILURE_HIGH_OVERTEMP ) = = 0 )
printk ( KERN_ERR " windfarm: Critical overtemp due to "
" immediate CPU temperature ! \n " ) ;
}
/*
* The first time around , initialize the array with the first
* temperature reading
*/
if ( first ) {
int i ;
cpu_thist_total = 0 ;
for ( i = 0 ; i < CPU_TEMP_HIST_SIZE ; i + + ) {
cpu_thist [ i ] = temp ;
cpu_thist_total + = temp ;
}
first = false ;
}
/*
* We calculate a history of max temperatures and use that for the
* overtemp management
*/
t_old = cpu_thist [ cpu_thist_pt ] ;
cpu_thist [ cpu_thist_pt ] = temp ;
cpu_thist_pt = ( cpu_thist_pt + 1 ) % CPU_TEMP_HIST_SIZE ;
cpu_thist_total - = t_old ;
cpu_thist_total + = temp ;
t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE ;
DBG_LOTS ( " t_avg = %d.%03d (out: %d.%03d, in: %d.%03d) \n " ,
FIX32TOPRINT ( t_avg ) , FIX32TOPRINT ( t_old ) , FIX32TOPRINT ( temp ) ) ;
/* Now check for average overtemps */
if ( t_avg > = ( cpu_all_tmax + LOW_OVER_AVERAGE ) ) {
new_state | = FAILURE_LOW_OVERTEMP ;
if ( ( failure_state & FAILURE_LOW_OVERTEMP ) = = 0 )
printk ( KERN_ERR " windfarm: Overtemp due to average CPU "
" temperature ! \n " ) ;
}
if ( t_avg > = ( cpu_all_tmax + HIGH_OVER_AVERAGE ) ) {
new_state | = FAILURE_HIGH_OVERTEMP ;
if ( ( failure_state & FAILURE_HIGH_OVERTEMP ) = = 0 )
printk ( KERN_ERR " windfarm: Critical overtemp due to "
" average CPU temperature ! \n " ) ;
}
/* Now handle overtemp conditions. We don't currently use the windfarm
* overtemp handling core as it ' s not fully suited to the needs of those
* new machine . This will be fixed later .
*/
if ( new_state ) {
/* High overtemp -> immediate shutdown */
if ( new_state & FAILURE_HIGH_OVERTEMP )
machine_power_off ( ) ;
if ( ( failure_state & new_state ) ! = new_state )
cpu_max_all_fans ( ) ;
failure_state | = new_state ;
} else if ( ( failure_state & FAILURE_LOW_OVERTEMP ) & &
( temp < ( cpu_all_tmax + LOW_OVER_CLEAR ) ) ) {
printk ( KERN_ERR " windfarm: Overtemp condition cleared ! \n " ) ;
failure_state & = ~ FAILURE_LOW_OVERTEMP ;
}
return failure_state & ( FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP ) ;
}
static int read_one_cpu_vals ( int cpu , s32 * temp , s32 * power )
{
s32 dtemp , volts , amps ;
int rc ;
/* Get diode temperature */
rc = wf_sensor_get ( sens_cpu_temp [ cpu ] , & dtemp ) ;
if ( rc ) {
DBG ( " CPU%d: temp reading error ! \n " , cpu ) ;
return - EIO ;
}
DBG_LOTS ( " CPU%d: temp = %d.%03d \n " , cpu , FIX32TOPRINT ( ( dtemp ) ) ) ;
* temp = dtemp ;
/* Get voltage */
rc = wf_sensor_get ( sens_cpu_volts [ cpu ] , & volts ) ;
if ( rc ) {
DBG ( " CPU%d, volts reading error ! \n " , cpu ) ;
return - EIO ;
}
DBG_LOTS ( " CPU%d: volts = %d.%03d \n " , cpu , FIX32TOPRINT ( ( volts ) ) ) ;
/* Get current */
rc = wf_sensor_get ( sens_cpu_amps [ cpu ] , & amps ) ;
if ( rc ) {
DBG ( " CPU%d, current reading error ! \n " , cpu ) ;
return - EIO ;
}
DBG_LOTS ( " CPU%d: amps = %d.%03d \n " , cpu , FIX32TOPRINT ( ( amps ) ) ) ;
/* Calculate power */
/* Scale voltage and current raw sensor values according to fixed scales
* obtained in Darwin and calculate power from I and V
*/
* power = ( ( ( u64 ) volts ) * ( ( u64 ) amps ) ) > > 16 ;
DBG_LOTS ( " CPU%d: power = %d.%03d \n " , cpu , FIX32TOPRINT ( ( * power ) ) ) ;
return 0 ;
}
static void cpu_fans_tick_split ( void )
{
int err , cpu ;
s32 intake , temp , power , t_max = 0 ;
DBG_LOTS ( " * cpu fans_tick_split() \n " ) ;
for ( cpu = 0 ; cpu < nr_chips ; + + cpu ) {
struct wf_cpu_pid_state * sp = & cpu_pid [ cpu ] ;
/* Read current speed */
wf_control_get ( cpu_rear_fans [ cpu ] , & sp - > target ) ;
DBG_LOTS ( " CPU%d: cur_target = %d RPM \n " , cpu , sp - > target ) ;
err = read_one_cpu_vals ( cpu , & temp , & power ) ;
if ( err ) {
failure_state | = FAILURE_SENSOR ;
cpu_max_all_fans ( ) ;
return ;
}
/* Keep track of highest temp */
t_max = max ( t_max , temp ) ;
/* Handle possible overtemps */
if ( cpu_check_overtemp ( t_max ) )
return ;
/* Run PID */
wf_cpu_pid_run ( sp , power , temp ) ;
DBG_LOTS ( " CPU%d: target = %d RPM \n " , cpu , sp - > target ) ;
/* Apply result directly to exhaust fan */
err = wf_control_set ( cpu_rear_fans [ cpu ] , sp - > target ) ;
if ( err ) {
2019-10-18 06:18:32 +03:00
pr_warn ( " wf_pm72: Fan %s reports error %d \n " ,
cpu_rear_fans [ cpu ] - > name , err ) ;
2012-04-29 19:42:27 +04:00
failure_state | = FAILURE_FAN ;
break ;
}
/* Scale result for intake fan */
intake = ( sp - > target * CPU_INTAKE_SCALE ) > > 16 ;
DBG_LOTS ( " CPU%d: intake = %d RPM \n " , cpu , intake ) ;
err = wf_control_set ( cpu_front_fans [ cpu ] , intake ) ;
if ( err ) {
2019-10-18 06:18:32 +03:00
pr_warn ( " wf_pm72: Fan %s reports error %d \n " ,
cpu_front_fans [ cpu ] - > name , err ) ;
2012-04-29 19:42:27 +04:00
failure_state | = FAILURE_FAN ;
break ;
}
}
}
static void cpu_fans_tick_combined ( void )
{
s32 temp0 , power0 , temp1 , power1 , t_max = 0 ;
s32 temp , power , intake , pump ;
struct wf_control * pump0 , * pump1 ;
struct wf_cpu_pid_state * sp = & cpu_pid [ 0 ] ;
int err , cpu ;
DBG_LOTS ( " * cpu fans_tick_combined() \n " ) ;
/* Read current speed from cpu 0 */
wf_control_get ( cpu_rear_fans [ 0 ] , & sp - > target ) ;
DBG_LOTS ( " CPUs: cur_target = %d RPM \n " , sp - > target ) ;
/* Read values for both CPUs */
err = read_one_cpu_vals ( 0 , & temp0 , & power0 ) ;
if ( err ) {
failure_state | = FAILURE_SENSOR ;
cpu_max_all_fans ( ) ;
return ;
}
err = read_one_cpu_vals ( 1 , & temp1 , & power1 ) ;
if ( err ) {
failure_state | = FAILURE_SENSOR ;
cpu_max_all_fans ( ) ;
return ;
}
/* Keep track of highest temp */
t_max = max ( t_max , max ( temp0 , temp1 ) ) ;
/* Handle possible overtemps */
if ( cpu_check_overtemp ( t_max ) )
return ;
/* Use the max temp & power of both */
temp = max ( temp0 , temp1 ) ;
power = max ( power0 , power1 ) ;
/* Run PID */
wf_cpu_pid_run ( sp , power , temp ) ;
/* Scale result for intake fan */
intake = ( sp - > target * CPU_INTAKE_SCALE ) > > 16 ;
/* Same deal with pump speed */
pump0 = cpu_pumps [ 0 ] ;
pump1 = cpu_pumps [ 1 ] ;
if ( ! pump0 ) {
pump0 = pump1 ;
pump1 = NULL ;
}
pump = ( sp - > target * wf_control_get_max ( pump0 ) ) /
cpu_mpu_data [ 0 ] - > rmaxn_exhaust_fan ;
DBG_LOTS ( " CPUs: target = %d RPM \n " , sp - > target ) ;
DBG_LOTS ( " CPUs: intake = %d RPM \n " , intake ) ;
DBG_LOTS ( " CPUs: pump = %d RPM \n " , pump ) ;
for ( cpu = 0 ; cpu < nr_chips ; cpu + + ) {
err = wf_control_set ( cpu_rear_fans [ cpu ] , sp - > target ) ;
if ( err ) {
2019-10-18 06:18:32 +03:00
pr_warn ( " wf_pm72: Fan %s reports error %d \n " ,
cpu_rear_fans [ cpu ] - > name , err ) ;
2012-04-29 19:42:27 +04:00
failure_state | = FAILURE_FAN ;
}
err = wf_control_set ( cpu_front_fans [ cpu ] , intake ) ;
if ( err ) {
2019-10-18 06:18:32 +03:00
pr_warn ( " wf_pm72: Fan %s reports error %d \n " ,
cpu_front_fans [ cpu ] - > name , err ) ;
2012-04-29 19:42:27 +04:00
failure_state | = FAILURE_FAN ;
}
err = 0 ;
if ( cpu_pumps [ cpu ] )
err = wf_control_set ( cpu_pumps [ cpu ] , pump ) ;
if ( err ) {
2019-10-18 06:18:32 +03:00
pr_warn ( " wf_pm72: Pump %s reports error %d \n " ,
cpu_pumps [ cpu ] - > name , err ) ;
2012-04-29 19:42:27 +04:00
failure_state | = FAILURE_FAN ;
}
}
}
/* Implementation... */
static int cpu_setup_pid ( int cpu )
{
struct wf_cpu_pid_param pid ;
const struct mpu_data * mpu = cpu_mpu_data [ cpu ] ;
s32 tmax , ttarget , ptarget ;
int fmin , fmax , hsize ;
/* Get PID params from the appropriate MPU EEPROM */
tmax = mpu - > tmax < < 16 ;
ttarget = mpu - > ttarget < < 16 ;
ptarget = ( ( s32 ) ( mpu - > pmaxh - mpu - > padjmax ) ) < < 16 ;
DBG ( " wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d \n " ,
cpu , FIX32TOPRINT ( ttarget ) , FIX32TOPRINT ( tmax ) ) ;
/* We keep a global tmax for overtemp calculations */
if ( tmax < cpu_all_tmax )
cpu_all_tmax = tmax ;
/* Set PID min/max by using the rear fan min/max */
fmin = wf_control_get_min ( cpu_rear_fans [ cpu ] ) ;
fmax = wf_control_get_max ( cpu_rear_fans [ cpu ] ) ;
DBG ( " wf_72: CPU%d max RPM range = [%d..%d] \n " , cpu , fmin , fmax ) ;
/* History size */
hsize = min_t ( int , mpu - > tguardband , WF_PID_MAX_HISTORY ) ;
DBG ( " wf_72: CPU%d history size = %d \n " , cpu , hsize ) ;
/* Initialize PID loop */
pid . interval = 1 ; /* seconds */
pid . history_len = hsize ;
pid . gd = mpu - > pid_gd ;
pid . gp = mpu - > pid_gp ;
pid . gr = mpu - > pid_gr ;
pid . tmax = tmax ;
pid . ttarget = ttarget ;
pid . pmaxadj = ptarget ;
pid . min = fmin ;
pid . max = fmax ;
wf_cpu_pid_init ( & cpu_pid [ cpu ] , & pid ) ;
cpu_pid [ cpu ] . target = 1000 ;
return 0 ;
}
/* Backside/U3 fan */
static struct wf_pid_param backside_u3_param = {
. interval = 5 ,
. history_len = 2 ,
. gd = 40 < < 20 ,
. gp = 5 < < 20 ,
. gr = 0 ,
. itarget = 65 < < 16 ,
. additive = 1 ,
. min = 20 ,
. max = 100 ,
} ;
static struct wf_pid_param backside_u3h_param = {
. interval = 5 ,
. history_len = 2 ,
. gd = 20 < < 20 ,
. gp = 5 < < 20 ,
. gr = 0 ,
. itarget = 75 < < 16 ,
. additive = 1 ,
. min = 20 ,
. max = 100 ,
} ;
static void backside_fan_tick ( void )
{
s32 temp ;
int speed ;
int err ;
if ( ! backside_fan | | ! backside_temp | | ! backside_tick )
return ;
if ( - - backside_tick > 0 )
return ;
backside_tick = backside_pid . param . interval ;
DBG_LOTS ( " * backside fans tick \n " ) ;
/* Update fan speed from actual fans */
err = wf_control_get ( backside_fan , & speed ) ;
if ( ! err )
backside_pid . target = speed ;
err = wf_sensor_get ( backside_temp , & temp ) ;
if ( err ) {
printk ( KERN_WARNING " windfarm: U4 temp sensor error %d \n " ,
err ) ;
failure_state | = FAILURE_SENSOR ;
wf_control_set_max ( backside_fan ) ;
return ;
}
speed = wf_pid_run ( & backside_pid , temp ) ;
DBG_LOTS ( " backside PID temp=%d.%.3d speed=%d \n " ,
FIX32TOPRINT ( temp ) , speed ) ;
err = wf_control_set ( backside_fan , speed ) ;
if ( err ) {
printk ( KERN_WARNING " windfarm: backside fan error %d \n " , err ) ;
failure_state | = FAILURE_FAN ;
}
}
static void backside_setup_pid ( void )
{
/* first time initialize things */
s32 fmin = wf_control_get_min ( backside_fan ) ;
s32 fmax = wf_control_get_max ( backside_fan ) ;
struct wf_pid_param param ;
struct device_node * u3 ;
int u3h = 1 ; /* conservative by default */
u3 = of_find_node_by_path ( " /u3@0,f8000000 " ) ;
if ( u3 ! = NULL ) {
const u32 * vers = of_get_property ( u3 , " device-rev " , NULL ) ;
if ( vers )
if ( ( ( * vers ) & 0x3f ) < 0x34 )
u3h = 0 ;
of_node_put ( u3 ) ;
}
param = u3h ? backside_u3h_param : backside_u3_param ;
param . min = max ( param . min , fmin ) ;
param . max = min ( param . max , fmax ) ;
wf_pid_init ( & backside_pid , & param ) ;
backside_tick = 1 ;
pr_info ( " wf_pm72: Backside control loop started. \n " ) ;
}
/* Drive bay fan */
static const struct wf_pid_param drives_param = {
. interval = 5 ,
. history_len = 2 ,
. gd = 30 < < 20 ,
. gp = 5 < < 20 ,
. gr = 0 ,
. itarget = 40 < < 16 ,
. additive = 1 ,
. min = 300 ,
. max = 4000 ,
} ;
static void drives_fan_tick ( void )
{
s32 temp ;
int speed ;
int err ;
if ( ! drives_fan | | ! drives_temp | | ! drives_tick )
return ;
if ( - - drives_tick > 0 )
return ;
drives_tick = drives_pid . param . interval ;
DBG_LOTS ( " * drives fans tick \n " ) ;
/* Update fan speed from actual fans */
err = wf_control_get ( drives_fan , & speed ) ;
if ( ! err )
drives_pid . target = speed ;
err = wf_sensor_get ( drives_temp , & temp ) ;
if ( err ) {
2019-10-18 06:18:32 +03:00
pr_warn ( " wf_pm72: drive bay temp sensor error %d \n " , err ) ;
2012-04-29 19:42:27 +04:00
failure_state | = FAILURE_SENSOR ;
wf_control_set_max ( drives_fan ) ;
return ;
}
speed = wf_pid_run ( & drives_pid , temp ) ;
DBG_LOTS ( " drives PID temp=%d.%.3d speed=%d \n " ,
FIX32TOPRINT ( temp ) , speed ) ;
err = wf_control_set ( drives_fan , speed ) ;
if ( err ) {
printk ( KERN_WARNING " windfarm: drive bay fan error %d \n " , err ) ;
failure_state | = FAILURE_FAN ;
}
}
static void drives_setup_pid ( void )
{
/* first time initialize things */
s32 fmin = wf_control_get_min ( drives_fan ) ;
s32 fmax = wf_control_get_max ( drives_fan ) ;
struct wf_pid_param param = drives_param ;
param . min = max ( param . min , fmin ) ;
param . max = min ( param . max , fmax ) ;
wf_pid_init ( & drives_pid , & param ) ;
drives_tick = 1 ;
pr_info ( " wf_pm72: Drive bay control loop started. \n " ) ;
}
static void set_fail_state ( void )
{
cpu_max_all_fans ( ) ;
if ( backside_fan )
wf_control_set_max ( backside_fan ) ;
if ( slots_fan )
wf_control_set_max ( slots_fan ) ;
if ( drives_fan )
wf_control_set_max ( drives_fan ) ;
}
static void pm72_tick ( void )
{
int i , last_failure ;
if ( ! started ) {
2018-01-24 04:42:28 +03:00
started = true ;
2012-04-29 19:42:27 +04:00
printk ( KERN_INFO " windfarm: CPUs control loops started. \n " ) ;
for ( i = 0 ; i < nr_chips ; + + i ) {
if ( cpu_setup_pid ( i ) < 0 ) {
failure_state = FAILURE_PERM ;
set_fail_state ( ) ;
break ;
}
}
DBG_LOTS ( " cpu_all_tmax=%d.%03d \n " , FIX32TOPRINT ( cpu_all_tmax ) ) ;
backside_setup_pid ( ) ;
drives_setup_pid ( ) ;
/*
* We don ' t have the right stuff to drive the PCI fan
* so we fix it to a default value
*/
wf_control_set ( slots_fan , SLOTS_FAN_DEFAULT_PWM ) ;
# ifdef HACKED_OVERTEMP
cpu_all_tmax = 60 < < 16 ;
# endif
}
/* Permanent failure, bail out */
if ( failure_state & FAILURE_PERM )
return ;
/*
* Clear all failure bits except low overtemp which will be eventually
* cleared by the control loop itself
*/
last_failure = failure_state ;
failure_state & = FAILURE_LOW_OVERTEMP ;
if ( cpu_pid_combined )
cpu_fans_tick_combined ( ) ;
else
cpu_fans_tick_split ( ) ;
backside_fan_tick ( ) ;
drives_fan_tick ( ) ;
DBG_LOTS ( " last_failure: 0x%x, failure_state: %x \n " ,
last_failure , failure_state ) ;
/* Check for failures. Any failure causes cpufreq clamping */
if ( failure_state & & last_failure = = 0 & & cpufreq_clamp )
wf_control_set_max ( cpufreq_clamp ) ;
if ( failure_state = = 0 & & last_failure & & cpufreq_clamp )
wf_control_set_min ( cpufreq_clamp ) ;
/* That's it for now, we might want to deal with other failures
* differently in the future though
*/
}
static void pm72_new_control ( struct wf_control * ct )
{
bool all_controls ;
bool had_pump = cpu_pumps [ 0 ] | | cpu_pumps [ 1 ] ;
if ( ! strcmp ( ct - > name , " cpu-front-fan-0 " ) )
cpu_front_fans [ 0 ] = ct ;
else if ( ! strcmp ( ct - > name , " cpu-front-fan-1 " ) )
cpu_front_fans [ 1 ] = ct ;
else if ( ! strcmp ( ct - > name , " cpu-rear-fan-0 " ) )
cpu_rear_fans [ 0 ] = ct ;
else if ( ! strcmp ( ct - > name , " cpu-rear-fan-1 " ) )
cpu_rear_fans [ 1 ] = ct ;
else if ( ! strcmp ( ct - > name , " cpu-pump-0 " ) )
cpu_pumps [ 0 ] = ct ;
else if ( ! strcmp ( ct - > name , " cpu-pump-1 " ) )
cpu_pumps [ 1 ] = ct ;
else if ( ! strcmp ( ct - > name , " backside-fan " ) )
backside_fan = ct ;
else if ( ! strcmp ( ct - > name , " slots-fan " ) )
slots_fan = ct ;
else if ( ! strcmp ( ct - > name , " drive-bay-fan " ) )
drives_fan = ct ;
else if ( ! strcmp ( ct - > name , " cpufreq-clamp " ) )
cpufreq_clamp = ct ;
all_controls =
cpu_front_fans [ 0 ] & &
cpu_rear_fans [ 0 ] & &
backside_fan & &
slots_fan & &
drives_fan ;
if ( nr_chips > 1 )
all_controls & =
cpu_front_fans [ 1 ] & &
cpu_rear_fans [ 1 ] ;
have_all_controls = all_controls ;
if ( ( cpu_pumps [ 0 ] | | cpu_pumps [ 1 ] ) & & ! had_pump ) {
pr_info ( " wf_pm72: Liquid cooling pump(s) detected, "
" using new algorithm ! \n " ) ;
cpu_pid_combined = true ;
}
}
static void pm72_new_sensor ( struct wf_sensor * sr )
{
bool all_sensors ;
if ( ! strcmp ( sr - > name , " cpu-diode-temp-0 " ) )
sens_cpu_temp [ 0 ] = sr ;
else if ( ! strcmp ( sr - > name , " cpu-diode-temp-1 " ) )
sens_cpu_temp [ 1 ] = sr ;
else if ( ! strcmp ( sr - > name , " cpu-voltage-0 " ) )
sens_cpu_volts [ 0 ] = sr ;
else if ( ! strcmp ( sr - > name , " cpu-voltage-1 " ) )
sens_cpu_volts [ 1 ] = sr ;
else if ( ! strcmp ( sr - > name , " cpu-current-0 " ) )
sens_cpu_amps [ 0 ] = sr ;
else if ( ! strcmp ( sr - > name , " cpu-current-1 " ) )
sens_cpu_amps [ 1 ] = sr ;
else if ( ! strcmp ( sr - > name , " backside-temp " ) )
backside_temp = sr ;
else if ( ! strcmp ( sr - > name , " hd-temp " ) )
drives_temp = sr ;
all_sensors =
sens_cpu_temp [ 0 ] & &
sens_cpu_volts [ 0 ] & &
sens_cpu_amps [ 0 ] & &
backside_temp & &
drives_temp ;
if ( nr_chips > 1 )
all_sensors & =
sens_cpu_temp [ 1 ] & &
sens_cpu_volts [ 1 ] & &
sens_cpu_amps [ 1 ] ;
have_all_sensors = all_sensors ;
}
static int pm72_wf_notify ( struct notifier_block * self ,
unsigned long event , void * data )
{
switch ( event ) {
case WF_EVENT_NEW_SENSOR :
pm72_new_sensor ( data ) ;
break ;
case WF_EVENT_NEW_CONTROL :
pm72_new_control ( data ) ;
break ;
case WF_EVENT_TICK :
if ( have_all_controls & & have_all_sensors )
pm72_tick ( ) ;
}
return 0 ;
}
static struct notifier_block pm72_events = {
. notifier_call = pm72_wf_notify ,
} ;
static int wf_pm72_probe ( struct platform_device * dev )
{
wf_register_client ( & pm72_events ) ;
return 0 ;
}
2012-12-22 03:03:50 +04:00
static int wf_pm72_remove ( struct platform_device * dev )
2012-04-29 19:42:27 +04:00
{
wf_unregister_client ( & pm72_events ) ;
/* should release all sensors and controls */
return 0 ;
}
static struct platform_driver wf_pm72_driver = {
. probe = wf_pm72_probe ,
. remove = wf_pm72_remove ,
. driver = {
. name = " windfarm " ,
} ,
} ;
static int __init wf_pm72_init ( void )
{
struct device_node * cpu ;
int i ;
if ( ! of_machine_is_compatible ( " PowerMac7,2 " ) & &
! of_machine_is_compatible ( " PowerMac7,3 " ) )
return - ENODEV ;
/* Count the number of CPU cores */
nr_chips = 0 ;
2012-12-02 07:00:09 +04:00
for_each_node_by_type ( cpu , " cpu " )
2012-04-29 19:42:27 +04:00
+ + nr_chips ;
if ( nr_chips > NR_CHIPS )
nr_chips = NR_CHIPS ;
pr_info ( " windfarm: Initializing for desktop G5 with %d chips \n " ,
nr_chips ) ;
/* Get MPU data for each CPU */
for ( i = 0 ; i < nr_chips ; i + + ) {
cpu_mpu_data [ i ] = wf_get_mpu ( i ) ;
if ( ! cpu_mpu_data [ i ] ) {
pr_err ( " wf_pm72: Failed to find MPU data for CPU %d \n " , i ) ;
return - ENXIO ;
}
}
# ifdef MODULE
request_module ( " windfarm_fcu_controls " ) ;
request_module ( " windfarm_lm75_sensor " ) ;
request_module ( " windfarm_ad7417_sensor " ) ;
request_module ( " windfarm_max6690_sensor " ) ;
request_module ( " windfarm_cpufreq_clamp " ) ;
# endif /* MODULE */
platform_driver_register ( & wf_pm72_driver ) ;
return 0 ;
}
static void __exit wf_pm72_exit ( void )
{
platform_driver_unregister ( & wf_pm72_driver ) ;
}
module_init ( wf_pm72_init ) ;
module_exit ( wf_pm72_exit ) ;
MODULE_AUTHOR ( " Benjamin Herrenschmidt <benh@kernel.crashing.org> " ) ;
MODULE_DESCRIPTION ( " Thermal control for AGP PowerMac G5s " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:windfarm " ) ;