Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Windfarm PowerMac thermal control. iMac G5 iSight
0004  *
0005  * (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>
0006  *
0007  * Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin
0008  * Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>
0009  *
0010  * PowerMac12,1
0011  * ============
0012  *
0013  * The algorithm used is the PID control algorithm, used the same way
0014  * the published Darwin code does, using the same values that are
0015  * present in the Darwin 8.10 snapshot property lists (note however
0016  * that none of the code has been re-used, it's a complete
0017  * re-implementation
0018  *
0019  * There is two models using PowerMac12,1. Model 2 is iMac G5 iSight
0020  * 17" while Model 3 is iMac G5 20". They do have both the same
0021  * controls with a tiny difference. The control-ids of hard-drive-fan
0022  * and cpu-fan is swapped.
0023  *
0024  * Target Correction :
0025  *
0026  * controls have a target correction calculated as :
0027  *
0028  * new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value
0029  * new_value = max(new_value, max(new_min, 0))
0030  *
0031  * OD Fan control correction.
0032  *
0033  * # model_id: 2
0034  *   offset     : -19563152
0035  *   slope      :  1956315
0036  *
0037  * # model_id: 3
0038  *   offset     : -15650652
0039  *   slope      :  1565065
0040  *
0041  * HD Fan control correction.
0042  *
0043  * # model_id: 2
0044  *   offset     : -15650652
0045  *   slope      :  1565065
0046  *
0047  * # model_id: 3
0048  *   offset     : -19563152
0049  *   slope      :  1956315
0050  *
0051  * CPU Fan control correction.
0052  *
0053  * # model_id: 2
0054  *   offset     : -25431900
0055  *   slope      :  2543190
0056  *
0057  * # model_id: 3
0058  *   offset     : -15650652
0059  *   slope      :  1565065
0060  *
0061  * Target rubber-banding :
0062  *
0063  * Some controls have a target correction which depends on another
0064  * control value. The correction is computed in the following way :
0065  *
0066  * new_min = ref_value * slope + offset
0067  *
0068  * ref_value is the value of the reference control. If new_min is
0069  * greater than 0, then we correct the target value using :
0070  *
0071  * new_target = max (new_target, new_min >> 16)
0072  *
0073  * # model_id : 2
0074  *   control    : cpu-fan
0075  *   ref    : optical-drive-fan
0076  *   offset : -15650652
0077  *   slope  : 1565065
0078  *
0079  * # model_id : 3
0080  *   control    : optical-drive-fan
0081  *   ref    : hard-drive-fan
0082  *   offset : -32768000
0083  *   slope  : 65536
0084  *
0085  * In order to have the moste efficient correction with those
0086  * dependencies, we must trigger HD loop before OD loop before CPU
0087  * loop.
0088  *
0089  * The various control loops found in Darwin config file are:
0090  *
0091  * HD Fan control loop.
0092  *
0093  * # model_id: 2
0094  *   control        : hard-drive-fan
0095  *   sensor         : hard-drive-temp
0096  *   PID params     : G_d = 0x00000000
0097  *                    G_p = 0x002D70A3
0098  *                    G_r = 0x00019999
0099  *                    History = 2 entries
0100  *                    Input target = 0x370000
0101  *                    Interval = 5s
0102  *
0103  * # model_id: 3
0104  *   control        : hard-drive-fan
0105  *   sensor         : hard-drive-temp
0106  *   PID params     : G_d = 0x00000000
0107  *                    G_p = 0x002170A3
0108  *                    G_r = 0x00019999
0109  *                    History = 2 entries
0110  *                    Input target = 0x370000
0111  *                    Interval = 5s
0112  *
0113  * OD Fan control loop.
0114  *
0115  * # model_id: 2
0116  *   control        : optical-drive-fan
0117  *   sensor         : optical-drive-temp
0118  *   PID params     : G_d = 0x00000000
0119  *                    G_p = 0x001FAE14
0120  *                    G_r = 0x00019999
0121  *                    History = 2 entries
0122  *                    Input target = 0x320000
0123  *                    Interval = 5s
0124  *
0125  * # model_id: 3
0126  *   control        : optical-drive-fan
0127  *   sensor         : optical-drive-temp
0128  *   PID params     : G_d = 0x00000000
0129  *                    G_p = 0x001FAE14
0130  *                    G_r = 0x00019999
0131  *                    History = 2 entries
0132  *                    Input target = 0x320000
0133  *                    Interval = 5s
0134  *
0135  * GPU Fan control loop.
0136  *
0137  * # model_id: 2
0138  *   control        : hard-drive-fan
0139  *   sensor         : gpu-temp
0140  *   PID params     : G_d = 0x00000000
0141  *                    G_p = 0x002A6666
0142  *                    G_r = 0x00019999
0143  *                    History = 2 entries
0144  *                    Input target = 0x5A0000
0145  *                    Interval = 5s
0146  *
0147  * # model_id: 3
0148  *   control        : cpu-fan
0149  *   sensor         : gpu-temp
0150  *   PID params     : G_d = 0x00000000
0151  *                    G_p = 0x0010CCCC
0152  *                    G_r = 0x00019999
0153  *                    History = 2 entries
0154  *                    Input target = 0x500000
0155  *                    Interval = 5s
0156  *
0157  * KODIAK (aka northbridge) Fan control loop.
0158  *
0159  * # model_id: 2
0160  *   control        : optical-drive-fan
0161  *   sensor         : north-bridge-temp
0162  *   PID params     : G_d = 0x00000000
0163  *                    G_p = 0x003BD70A
0164  *                    G_r = 0x00019999
0165  *                    History = 2 entries
0166  *                    Input target = 0x550000
0167  *                    Interval = 5s
0168  *
0169  * # model_id: 3
0170  *   control        : hard-drive-fan
0171  *   sensor         : north-bridge-temp
0172  *   PID params     : G_d = 0x00000000
0173  *                    G_p = 0x0030F5C2
0174  *                    G_r = 0x00019999
0175  *                    History = 2 entries
0176  *                    Input target = 0x550000
0177  *                    Interval = 5s
0178  *
0179  * CPU Fan control loop.
0180  *
0181  *   control        : cpu-fan
0182  *   sensors        : cpu-temp, cpu-power
0183  *   PID params     : from SDB partition
0184  *
0185  * CPU Slew control loop.
0186  *
0187  *   control        : cpufreq-clamp
0188  *   sensor         : cpu-temp
0189  */
0190 
0191 #undef  DEBUG
0192 
0193 #include <linux/types.h>
0194 #include <linux/errno.h>
0195 #include <linux/kernel.h>
0196 #include <linux/delay.h>
0197 #include <linux/slab.h>
0198 #include <linux/init.h>
0199 #include <linux/spinlock.h>
0200 #include <linux/wait.h>
0201 #include <linux/kmod.h>
0202 #include <linux/device.h>
0203 #include <linux/platform_device.h>
0204 #include <linux/of.h>
0205 
0206 #include <asm/machdep.h>
0207 #include <asm/io.h>
0208 #include <asm/sections.h>
0209 #include <asm/smu.h>
0210 
0211 #include "windfarm.h"
0212 #include "windfarm_pid.h"
0213 
0214 #define VERSION "0.3"
0215 
0216 static int pm121_mach_model;    /* machine model id */
0217 
0218 /* Controls & sensors */
0219 static struct wf_sensor *sensor_cpu_power;
0220 static struct wf_sensor *sensor_cpu_temp;
0221 static struct wf_sensor *sensor_cpu_voltage;
0222 static struct wf_sensor *sensor_cpu_current;
0223 static struct wf_sensor *sensor_gpu_temp;
0224 static struct wf_sensor *sensor_north_bridge_temp;
0225 static struct wf_sensor *sensor_hard_drive_temp;
0226 static struct wf_sensor *sensor_optical_drive_temp;
0227 static struct wf_sensor *sensor_incoming_air_temp; /* unused ! */
0228 
0229 enum {
0230     FAN_CPU,
0231     FAN_HD,
0232     FAN_OD,
0233     CPUFREQ,
0234     N_CONTROLS
0235 };
0236 static struct wf_control *controls[N_CONTROLS] = {};
0237 
0238 /* Set to kick the control loop into life */
0239 static int pm121_all_controls_ok, pm121_all_sensors_ok;
0240 static bool pm121_started;
0241 
0242 enum {
0243     FAILURE_FAN     = 1 << 0,
0244     FAILURE_SENSOR      = 1 << 1,
0245     FAILURE_OVERTEMP    = 1 << 2
0246 };
0247 
0248 /* All sys loops. Note the HD before the OD loop in order to have it
0249    run before. */
0250 enum {
0251     LOOP_GPU,       /* control = hd or cpu, but luckily,
0252                    it doesn't matter */
0253     LOOP_HD,        /* control = hd */
0254     LOOP_KODIAK,        /* control = hd or od */
0255     LOOP_OD,        /* control = od */
0256     N_LOOPS
0257 };
0258 
0259 static const char *loop_names[N_LOOPS] = {
0260     "GPU",
0261     "HD",
0262     "KODIAK",
0263     "OD",
0264 };
0265 
0266 #define PM121_NUM_CONFIGS   2
0267 
0268 static unsigned int pm121_failure_state;
0269 static int pm121_readjust, pm121_skipping;
0270 static bool pm121_overtemp;
0271 static s32 average_power;
0272 
0273 struct pm121_correction {
0274     int offset;
0275     int slope;
0276 };
0277 
0278 static struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
0279     /* FAN_OD */
0280     {
0281         /* MODEL 2 */
0282         { .offset   = -19563152,
0283           .slope    =  1956315
0284         },
0285         /* MODEL 3 */
0286         { .offset   = -15650652,
0287           .slope    =  1565065
0288         },
0289     },
0290     /* FAN_HD */
0291     {
0292         /* MODEL 2 */
0293         { .offset   = -15650652,
0294           .slope    =  1565065
0295         },
0296         /* MODEL 3 */
0297         { .offset   = -19563152,
0298           .slope    =  1956315
0299         },
0300     },
0301     /* FAN_CPU */
0302     {
0303         /* MODEL 2 */
0304         { .offset   = -25431900,
0305           .slope    =  2543190
0306         },
0307         /* MODEL 3 */
0308         { .offset   = -15650652,
0309           .slope    =  1565065
0310         },
0311     },
0312     /* CPUFREQ has no correction (and is not implemented at all) */
0313 };
0314 
0315 struct pm121_connection {
0316     unsigned int    control_id;
0317     unsigned int    ref_id;
0318     struct pm121_correction correction;
0319 };
0320 
0321 static struct pm121_connection pm121_connections[] = {
0322     /* MODEL 2 */
0323     { .control_id   = FAN_CPU,
0324       .ref_id   = FAN_OD,
0325       { .offset = -32768000,
0326         .slope  =  65536
0327       }
0328     },
0329     /* MODEL 3 */
0330     { .control_id   = FAN_OD,
0331       .ref_id   = FAN_HD,
0332       { .offset = -32768000,
0333         .slope  =  65536
0334       }
0335     },
0336 };
0337 
0338 /* pointer to the current model connection */
0339 static struct pm121_connection *pm121_connection;
0340 
0341 /*
0342  * ****** System Fans Control Loop ******
0343  *
0344  */
0345 
0346 /* Since each loop handles only one control and we want to avoid
0347  * writing virtual control, we store the control correction with the
0348  * loop params. Some data are not set, there are common to all loop
0349  * and thus, hardcoded.
0350  */
0351 struct pm121_sys_param {
0352     /* purely informative since we use mach_model-2 as index */
0353     int         model_id;
0354     struct wf_sensor    **sensor; /* use sensor_id instead ? */
0355     s32         gp, itarget;
0356     unsigned int        control_id;
0357 };
0358 
0359 static struct pm121_sys_param
0360 pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
0361     /* GPU Fan control loop */
0362     {
0363         { .model_id = 2,
0364           .sensor   = &sensor_gpu_temp,
0365           .gp       = 0x002A6666,
0366           .itarget  = 0x5A0000,
0367           .control_id   = FAN_HD,
0368         },
0369         { .model_id = 3,
0370           .sensor   = &sensor_gpu_temp,
0371           .gp       = 0x0010CCCC,
0372           .itarget  = 0x500000,
0373           .control_id   = FAN_CPU,
0374         },
0375     },
0376     /* HD Fan control loop */
0377     {
0378         { .model_id = 2,
0379           .sensor   = &sensor_hard_drive_temp,
0380           .gp       = 0x002D70A3,
0381           .itarget  = 0x370000,
0382           .control_id   = FAN_HD,
0383         },
0384         { .model_id = 3,
0385           .sensor   = &sensor_hard_drive_temp,
0386           .gp       = 0x002170A3,
0387           .itarget  = 0x370000,
0388           .control_id   = FAN_HD,
0389         },
0390     },
0391     /* KODIAK Fan control loop */
0392     {
0393         { .model_id = 2,
0394           .sensor   = &sensor_north_bridge_temp,
0395           .gp       = 0x003BD70A,
0396           .itarget  = 0x550000,
0397           .control_id   = FAN_OD,
0398         },
0399         { .model_id = 3,
0400           .sensor   = &sensor_north_bridge_temp,
0401           .gp       = 0x0030F5C2,
0402           .itarget  = 0x550000,
0403           .control_id   = FAN_HD,
0404         },
0405     },
0406     /* OD Fan control loop */
0407     {
0408         { .model_id = 2,
0409           .sensor   = &sensor_optical_drive_temp,
0410           .gp       = 0x001FAE14,
0411           .itarget  = 0x320000,
0412           .control_id   = FAN_OD,
0413         },
0414         { .model_id = 3,
0415           .sensor   = &sensor_optical_drive_temp,
0416           .gp       = 0x001FAE14,
0417           .itarget  = 0x320000,
0418           .control_id   = FAN_OD,
0419         },
0420     },
0421 };
0422 
0423 /* the hardcoded values */
0424 #define PM121_SYS_GD        0x00000000
0425 #define PM121_SYS_GR        0x00019999
0426 #define PM121_SYS_HISTORY_SIZE  2
0427 #define PM121_SYS_INTERVAL  5
0428 
0429 /* State data used by the system fans control loop
0430  */
0431 struct pm121_sys_state {
0432     int         ticks;
0433     s32         setpoint;
0434     struct wf_pid_state pid;
0435 };
0436 
0437 static struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
0438 
0439 /*
0440  * ****** CPU Fans Control Loop ******
0441  *
0442  */
0443 
0444 #define PM121_CPU_INTERVAL  1
0445 
0446 /* State data used by the cpu fans control loop
0447  */
0448 struct pm121_cpu_state {
0449     int         ticks;
0450     s32         setpoint;
0451     struct wf_cpu_pid_state pid;
0452 };
0453 
0454 static struct pm121_cpu_state *pm121_cpu_state;
0455 
0456 
0457 
0458 /*
0459  * ***** Implementation *****
0460  *
0461  */
0462 
0463 /* correction the value using the output-low-bound correction algo */
0464 static s32 pm121_correct(s32 new_setpoint,
0465              unsigned int control_id,
0466              s32 min)
0467 {
0468     s32 new_min;
0469     struct pm121_correction *correction;
0470     correction = &corrections[control_id][pm121_mach_model - 2];
0471 
0472     new_min = (average_power * correction->slope) >> 16;
0473     new_min += correction->offset;
0474     new_min = (new_min >> 16) + min;
0475 
0476     return max3(new_setpoint, new_min, 0);
0477 }
0478 
0479 static s32 pm121_connect(unsigned int control_id, s32 setpoint)
0480 {
0481     s32 new_min, value, new_setpoint;
0482 
0483     if (pm121_connection->control_id == control_id) {
0484         controls[control_id]->ops->get_value(controls[control_id],
0485                              &value);
0486         new_min = value * pm121_connection->correction.slope;
0487         new_min += pm121_connection->correction.offset;
0488         if (new_min > 0) {
0489             new_setpoint = max(setpoint, (new_min >> 16));
0490             if (new_setpoint != setpoint) {
0491                 pr_debug("pm121: %s depending on %s, "
0492                      "corrected from %d to %d RPM\n",
0493                      controls[control_id]->name,
0494                      controls[pm121_connection->ref_id]->name,
0495                      (int) setpoint, (int) new_setpoint);
0496             }
0497         } else
0498             new_setpoint = setpoint;
0499     }
0500     /* no connection */
0501     else
0502         new_setpoint = setpoint;
0503 
0504     return new_setpoint;
0505 }
0506 
0507 /* FAN LOOPS */
0508 static void pm121_create_sys_fans(int loop_id)
0509 {
0510     struct pm121_sys_param *param = NULL;
0511     struct wf_pid_param pid_param;
0512     struct wf_control *control = NULL;
0513     int i;
0514 
0515     /* First, locate the params for this model */
0516     for (i = 0; i < PM121_NUM_CONFIGS; i++) {
0517         if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
0518             param = &(pm121_sys_all_params[loop_id][i]);
0519             break;
0520         }
0521     }
0522 
0523     /* No params found, put fans to max */
0524     if (param == NULL) {
0525         printk(KERN_WARNING "pm121: %s fan config not found "
0526                " for this machine model\n",
0527                loop_names[loop_id]);
0528         goto fail;
0529     }
0530 
0531     control = controls[param->control_id];
0532 
0533     /* Alloc & initialize state */
0534     pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
0535                        GFP_KERNEL);
0536     if (pm121_sys_state[loop_id] == NULL) {
0537         printk(KERN_WARNING "pm121: Memory allocation error\n");
0538         goto fail;
0539     }
0540     pm121_sys_state[loop_id]->ticks = 1;
0541 
0542     /* Fill PID params */
0543     pid_param.gd        = PM121_SYS_GD;
0544     pid_param.gp        = param->gp;
0545     pid_param.gr        = PM121_SYS_GR;
0546     pid_param.interval  = PM121_SYS_INTERVAL;
0547     pid_param.history_len   = PM121_SYS_HISTORY_SIZE;
0548     pid_param.itarget   = param->itarget;
0549     if(control)
0550     {
0551         pid_param.min       = control->ops->get_min(control);
0552         pid_param.max       = control->ops->get_max(control);
0553     } else {
0554         /*
0555          * This is probably not the right!?
0556          * Perhaps goto fail  if control == NULL  above?
0557          */
0558         pid_param.min       = 0;
0559         pid_param.max       = 0;
0560     }
0561 
0562     wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
0563 
0564     pr_debug("pm121: %s Fan control loop initialized.\n"
0565          "       itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
0566          loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
0567          pid_param.min, pid_param.max);
0568     return;
0569 
0570  fail:
0571     /* note that this is not optimal since another loop may still
0572        control the same control */
0573     printk(KERN_WARNING "pm121: failed to set up %s loop "
0574            "setting \"%s\" to max speed.\n",
0575            loop_names[loop_id], control ? control->name : "uninitialized value");
0576 
0577     if (control)
0578         wf_control_set_max(control);
0579 }
0580 
0581 static void pm121_sys_fans_tick(int loop_id)
0582 {
0583     struct pm121_sys_param *param;
0584     struct pm121_sys_state *st;
0585     struct wf_sensor *sensor;
0586     struct wf_control *control;
0587     s32 temp, new_setpoint;
0588     int rc;
0589 
0590     param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
0591     st = pm121_sys_state[loop_id];
0592     sensor = *(param->sensor);
0593     control = controls[param->control_id];
0594 
0595     if (--st->ticks != 0) {
0596         if (pm121_readjust)
0597             goto readjust;
0598         return;
0599     }
0600     st->ticks = PM121_SYS_INTERVAL;
0601 
0602     rc = sensor->ops->get_value(sensor, &temp);
0603     if (rc) {
0604         printk(KERN_WARNING "windfarm: %s sensor error %d\n",
0605                sensor->name, rc);
0606         pm121_failure_state |= FAILURE_SENSOR;
0607         return;
0608     }
0609 
0610     pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",
0611          loop_names[loop_id], sensor->name,
0612          FIX32TOPRINT(temp));
0613 
0614     new_setpoint = wf_pid_run(&st->pid, temp);
0615 
0616     /* correction */
0617     new_setpoint = pm121_correct(new_setpoint,
0618                      param->control_id,
0619                      st->pid.param.min);
0620     /* linked corretion */
0621     new_setpoint = pm121_connect(param->control_id, new_setpoint);
0622 
0623     if (new_setpoint == st->setpoint)
0624         return;
0625     st->setpoint = new_setpoint;
0626     pr_debug("pm121: %s corrected setpoint: %d RPM\n",
0627          control->name, (int)new_setpoint);
0628  readjust:
0629     if (control && pm121_failure_state == 0) {
0630         rc = control->ops->set_value(control, st->setpoint);
0631         if (rc) {
0632             printk(KERN_WARNING "windfarm: %s fan error %d\n",
0633                    control->name, rc);
0634             pm121_failure_state |= FAILURE_FAN;
0635         }
0636     }
0637 }
0638 
0639 
0640 /* CPU LOOP */
0641 static void pm121_create_cpu_fans(void)
0642 {
0643     struct wf_cpu_pid_param pid_param;
0644     const struct smu_sdbp_header *hdr;
0645     struct smu_sdbp_cpupiddata *piddata;
0646     struct smu_sdbp_fvt *fvt;
0647     struct wf_control *fan_cpu;
0648     s32 tmax, tdelta, maxpow, powadj;
0649 
0650     fan_cpu = controls[FAN_CPU];
0651 
0652     /* First, locate the PID params in SMU SBD */
0653     hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
0654     if (hdr == 0) {
0655         printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
0656         goto fail;
0657     }
0658     piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
0659 
0660     /* Get the FVT params for operating point 0 (the only supported one
0661      * for now) in order to get tmax
0662      */
0663     hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
0664     if (hdr) {
0665         fvt = (struct smu_sdbp_fvt *)&hdr[1];
0666         tmax = ((s32)fvt->maxtemp) << 16;
0667     } else
0668         tmax = 0x5e0000; /* 94 degree default */
0669 
0670     /* Alloc & initialize state */
0671     pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
0672                   GFP_KERNEL);
0673     if (pm121_cpu_state == NULL)
0674         goto fail;
0675     pm121_cpu_state->ticks = 1;
0676 
0677     /* Fill PID params */
0678     pid_param.interval = PM121_CPU_INTERVAL;
0679     pid_param.history_len = piddata->history_len;
0680     if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
0681         printk(KERN_WARNING "pm121: History size overflow on "
0682                "CPU control loop (%d)\n", piddata->history_len);
0683         pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
0684     }
0685     pid_param.gd = piddata->gd;
0686     pid_param.gp = piddata->gp;
0687     pid_param.gr = piddata->gr / pid_param.history_len;
0688 
0689     tdelta = ((s32)piddata->target_temp_delta) << 16;
0690     maxpow = ((s32)piddata->max_power) << 16;
0691     powadj = ((s32)piddata->power_adj) << 16;
0692 
0693     pid_param.tmax = tmax;
0694     pid_param.ttarget = tmax - tdelta;
0695     pid_param.pmaxadj = maxpow - powadj;
0696 
0697     pid_param.min = fan_cpu->ops->get_min(fan_cpu);
0698     pid_param.max = fan_cpu->ops->get_max(fan_cpu);
0699 
0700     wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
0701 
0702     pr_debug("pm121: CPU Fan control initialized.\n");
0703     pr_debug("       ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
0704          FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
0705          pid_param.min, pid_param.max);
0706 
0707     return;
0708 
0709  fail:
0710     printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
0711 
0712     if (controls[CPUFREQ])
0713         wf_control_set_max(controls[CPUFREQ]);
0714     if (fan_cpu)
0715         wf_control_set_max(fan_cpu);
0716 }
0717 
0718 
0719 static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
0720 {
0721     s32 new_setpoint, temp, power;
0722     struct wf_control *fan_cpu = NULL;
0723     int rc;
0724 
0725     if (--st->ticks != 0) {
0726         if (pm121_readjust)
0727             goto readjust;
0728         return;
0729     }
0730     st->ticks = PM121_CPU_INTERVAL;
0731 
0732     fan_cpu = controls[FAN_CPU];
0733 
0734     rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
0735     if (rc) {
0736         printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
0737                rc);
0738         pm121_failure_state |= FAILURE_SENSOR;
0739         return;
0740     }
0741 
0742     rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
0743     if (rc) {
0744         printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
0745                rc);
0746         pm121_failure_state |= FAILURE_SENSOR;
0747         return;
0748     }
0749 
0750     pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
0751          FIX32TOPRINT(temp), FIX32TOPRINT(power));
0752 
0753     if (temp > st->pid.param.tmax)
0754         pm121_failure_state |= FAILURE_OVERTEMP;
0755 
0756     new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
0757 
0758     /* correction */
0759     new_setpoint = pm121_correct(new_setpoint,
0760                      FAN_CPU,
0761                      st->pid.param.min);
0762 
0763     /* connected correction */
0764     new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
0765 
0766     if (st->setpoint == new_setpoint)
0767         return;
0768     st->setpoint = new_setpoint;
0769     pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
0770 
0771  readjust:
0772     if (fan_cpu && pm121_failure_state == 0) {
0773         rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
0774         if (rc) {
0775             printk(KERN_WARNING "pm121: %s fan error %d\n",
0776                    fan_cpu->name, rc);
0777             pm121_failure_state |= FAILURE_FAN;
0778         }
0779     }
0780 }
0781 
0782 /*
0783  * ****** Common ******
0784  *
0785  */
0786 
0787 static void pm121_tick(void)
0788 {
0789     unsigned int last_failure = pm121_failure_state;
0790     unsigned int new_failure;
0791     s32 total_power;
0792     int i;
0793 
0794     if (!pm121_started) {
0795         pr_debug("pm121: creating control loops !\n");
0796         for (i = 0; i < N_LOOPS; i++)
0797             pm121_create_sys_fans(i);
0798 
0799         pm121_create_cpu_fans();
0800         pm121_started = true;
0801     }
0802 
0803     /* skipping ticks */
0804     if (pm121_skipping && --pm121_skipping)
0805         return;
0806 
0807     /* compute average power */
0808     total_power = 0;
0809     for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
0810         total_power += pm121_cpu_state->pid.powers[i];
0811 
0812     average_power = total_power / pm121_cpu_state->pid.param.history_len;
0813 
0814 
0815     pm121_failure_state = 0;
0816     for (i = 0 ; i < N_LOOPS; i++) {
0817         if (pm121_sys_state[i])
0818             pm121_sys_fans_tick(i);
0819     }
0820 
0821     if (pm121_cpu_state)
0822         pm121_cpu_fans_tick(pm121_cpu_state);
0823 
0824     pm121_readjust = 0;
0825     new_failure = pm121_failure_state & ~last_failure;
0826 
0827     /* If entering failure mode, clamp cpufreq and ramp all
0828      * fans to full speed.
0829      */
0830     if (pm121_failure_state && !last_failure) {
0831         for (i = 0; i < N_CONTROLS; i++) {
0832             if (controls[i])
0833                 wf_control_set_max(controls[i]);
0834         }
0835     }
0836 
0837     /* If leaving failure mode, unclamp cpufreq and readjust
0838      * all fans on next iteration
0839      */
0840     if (!pm121_failure_state && last_failure) {
0841         if (controls[CPUFREQ])
0842             wf_control_set_min(controls[CPUFREQ]);
0843         pm121_readjust = 1;
0844     }
0845 
0846     /* Overtemp condition detected, notify and start skipping a couple
0847      * ticks to let the temperature go down
0848      */
0849     if (new_failure & FAILURE_OVERTEMP) {
0850         wf_set_overtemp();
0851         pm121_skipping = 2;
0852         pm121_overtemp = true;
0853     }
0854 
0855     /* We only clear the overtemp condition if overtemp is cleared
0856      * _and_ no other failure is present. Since a sensor error will
0857      * clear the overtemp condition (can't measure temperature) at
0858      * the control loop levels, but we don't want to keep it clear
0859      * here in this case
0860      */
0861     if (!pm121_failure_state && pm121_overtemp) {
0862         wf_clear_overtemp();
0863         pm121_overtemp = false;
0864     }
0865 }
0866 
0867 
0868 static struct wf_control* pm121_register_control(struct wf_control *ct,
0869                          const char *match,
0870                          unsigned int id)
0871 {
0872     if (controls[id] == NULL && !strcmp(ct->name, match)) {
0873         if (wf_get_control(ct) == 0)
0874             controls[id] = ct;
0875     }
0876     return controls[id];
0877 }
0878 
0879 static void pm121_new_control(struct wf_control *ct)
0880 {
0881     int all = 1;
0882 
0883     if (pm121_all_controls_ok)
0884         return;
0885 
0886     all = pm121_register_control(ct, "optical-drive-fan", FAN_OD) && all;
0887     all = pm121_register_control(ct, "hard-drive-fan", FAN_HD) && all;
0888     all = pm121_register_control(ct, "cpu-fan", FAN_CPU) && all;
0889     all = pm121_register_control(ct, "cpufreq-clamp", CPUFREQ) && all;
0890 
0891     if (all)
0892         pm121_all_controls_ok = 1;
0893 }
0894 
0895 
0896 
0897 
0898 static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,
0899                            const char *match,
0900                            struct wf_sensor **var)
0901 {
0902     if (*var == NULL && !strcmp(sensor->name, match)) {
0903         if (wf_get_sensor(sensor) == 0)
0904             *var = sensor;
0905     }
0906     return *var;
0907 }
0908 
0909 static void pm121_new_sensor(struct wf_sensor *sr)
0910 {
0911     int all = 1;
0912 
0913     if (pm121_all_sensors_ok)
0914         return;
0915 
0916     all = pm121_register_sensor(sr, "cpu-temp",
0917                     &sensor_cpu_temp) && all;
0918     all = pm121_register_sensor(sr, "cpu-current",
0919                     &sensor_cpu_current) && all;
0920     all = pm121_register_sensor(sr, "cpu-voltage",
0921                     &sensor_cpu_voltage) && all;
0922     all = pm121_register_sensor(sr, "cpu-power",
0923                     &sensor_cpu_power) && all;
0924     all = pm121_register_sensor(sr, "hard-drive-temp",
0925                     &sensor_hard_drive_temp) && all;
0926     all = pm121_register_sensor(sr, "optical-drive-temp",
0927                     &sensor_optical_drive_temp) && all;
0928     all = pm121_register_sensor(sr, "incoming-air-temp",
0929                     &sensor_incoming_air_temp) && all;
0930     all = pm121_register_sensor(sr, "north-bridge-temp",
0931                     &sensor_north_bridge_temp) && all;
0932     all = pm121_register_sensor(sr, "gpu-temp",
0933                     &sensor_gpu_temp) && all;
0934 
0935     if (all)
0936         pm121_all_sensors_ok = 1;
0937 }
0938 
0939 
0940 
0941 static int pm121_notify(struct notifier_block *self,
0942             unsigned long event, void *data)
0943 {
0944     switch (event) {
0945     case WF_EVENT_NEW_CONTROL:
0946         pr_debug("pm121: new control %s detected\n",
0947              ((struct wf_control *)data)->name);
0948         pm121_new_control(data);
0949         break;
0950     case WF_EVENT_NEW_SENSOR:
0951         pr_debug("pm121: new sensor %s detected\n",
0952              ((struct wf_sensor *)data)->name);
0953         pm121_new_sensor(data);
0954         break;
0955     case WF_EVENT_TICK:
0956         if (pm121_all_controls_ok && pm121_all_sensors_ok)
0957             pm121_tick();
0958         break;
0959     }
0960 
0961     return 0;
0962 }
0963 
0964 static struct notifier_block pm121_events = {
0965     .notifier_call  = pm121_notify,
0966 };
0967 
0968 static int pm121_init_pm(void)
0969 {
0970     const struct smu_sdbp_header *hdr;
0971 
0972     hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
0973     if (hdr != 0) {
0974         struct smu_sdbp_sensortree *st =
0975             (struct smu_sdbp_sensortree *)&hdr[1];
0976         pm121_mach_model = st->model_id;
0977     }
0978 
0979     pm121_connection = &pm121_connections[pm121_mach_model - 2];
0980 
0981     printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
0982            pm121_mach_model);
0983 
0984     return 0;
0985 }
0986 
0987 
0988 static int pm121_probe(struct platform_device *ddev)
0989 {
0990     wf_register_client(&pm121_events);
0991 
0992     return 0;
0993 }
0994 
0995 static int pm121_remove(struct platform_device *ddev)
0996 {
0997     wf_unregister_client(&pm121_events);
0998     return 0;
0999 }
1000 
1001 static struct platform_driver pm121_driver = {
1002     .probe = pm121_probe,
1003     .remove = pm121_remove,
1004     .driver = {
1005         .name = "windfarm",
1006         .bus = &platform_bus_type,
1007     },
1008 };
1009 
1010 
1011 static int __init pm121_init(void)
1012 {
1013     int rc = -ENODEV;
1014 
1015     if (of_machine_is_compatible("PowerMac12,1"))
1016         rc = pm121_init_pm();
1017 
1018     if (rc == 0) {
1019         request_module("windfarm_smu_controls");
1020         request_module("windfarm_smu_sensors");
1021         request_module("windfarm_smu_sat");
1022         request_module("windfarm_lm75_sensor");
1023         request_module("windfarm_max6690_sensor");
1024         request_module("windfarm_cpufreq_clamp");
1025         platform_driver_register(&pm121_driver);
1026     }
1027 
1028     return rc;
1029 }
1030 
1031 static void __exit pm121_exit(void)
1032 {
1033 
1034     platform_driver_unregister(&pm121_driver);
1035 }
1036 
1037 
1038 module_init(pm121_init);
1039 module_exit(pm121_exit);
1040 
1041 MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
1042 MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
1043 MODULE_LICENSE("GPL");
1044