Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Windfarm PowerMac thermal control. iMac G5
0004  *
0005  * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
0006  *                    <benh@kernel.crashing.org>
0007  *
0008  * The algorithm used is the PID control algorithm, used the same
0009  * way the published Darwin code does, using the same values that
0010  * are present in the Darwin 8.2 snapshot property lists (note however
0011  * that none of the code has been re-used, it's a complete re-implementation
0012  *
0013  * The various control loops found in Darwin config file are:
0014  *
0015  * PowerMac8,1 and PowerMac8,2
0016  * ===========================
0017  *
0018  * System Fans control loop. Different based on models. In addition to the
0019  * usual PID algorithm, the control loop gets 2 additional pairs of linear
0020  * scaling factors (scale/offsets) expressed as 4.12 fixed point values
0021  * signed offset, unsigned scale)
0022  *
0023  * The targets are modified such as:
0024  *  - the linked control (second control) gets the target value as-is
0025  *    (typically the drive fan)
0026  *  - the main control (first control) gets the target value scaled with
0027  *    the first pair of factors, and is then modified as below
0028  *  - the value of the target of the CPU Fan control loop is retrieved,
0029  *    scaled with the second pair of factors, and the max of that and
0030  *    the scaled target is applied to the main control.
0031  *
0032  * # model_id: 2
0033  *   controls       : system-fan, drive-bay-fan
0034  *   sensors        : hd-temp
0035  *   PID params     : G_d = 0x15400000
0036  *                    G_p = 0x00200000
0037  *                    G_r = 0x000002fd
0038  *                    History = 2 entries
0039  *                    Input target = 0x3a0000
0040  *                    Interval = 5s
0041  *   linear-factors : offset = 0xff38 scale  = 0x0ccd
0042  *                    offset = 0x0208 scale  = 0x07ae
0043  *
0044  * # model_id: 3
0045  *   controls       : system-fan, drive-bay-fan
0046  *   sensors        : hd-temp
0047  *   PID params     : G_d = 0x08e00000
0048  *                    G_p = 0x00566666
0049  *                    G_r = 0x0000072b
0050  *                    History = 2 entries
0051  *                    Input target = 0x350000
0052  *                    Interval = 5s
0053  *   linear-factors : offset = 0xff38 scale  = 0x0ccd
0054  *                    offset = 0x0000 scale  = 0x0000
0055  *
0056  * # model_id: 5
0057  *   controls       : system-fan
0058  *   sensors        : hd-temp
0059  *   PID params     : G_d = 0x15400000
0060  *                    G_p = 0x00233333
0061  *                    G_r = 0x000002fd
0062  *                    History = 2 entries
0063  *                    Input target = 0x3a0000
0064  *                    Interval = 5s
0065  *   linear-factors : offset = 0x0000 scale  = 0x1000
0066  *                    offset = 0x0091 scale  = 0x0bae
0067  *
0068  * CPU Fan control loop. The loop is identical for all models. it
0069  * has an additional pair of scaling factor. This is used to scale the
0070  * systems fan control loop target result (the one before it gets scaled
0071  * by the System Fans control loop itself). Then, the max value of the
0072  * calculated target value and system fan value is sent to the fans
0073  *
0074  *   controls       : cpu-fan
0075  *   sensors        : cpu-temp cpu-power
0076  *   PID params     : From SMU sdb partition
0077  *   linear-factors : offset = 0xfb50 scale  = 0x1000
0078  *
0079  * CPU Slew control loop. Not implemented. The cpufreq driver in linux is
0080  * completely separate for now, though we could find a way to link it, either
0081  * as a client reacting to overtemp notifications, or directling monitoring
0082  * the CPU temperature
0083  *
0084  * WARNING ! The CPU control loop requires the CPU tmax for the current
0085  * operating point. However, we currently are completely separated from
0086  * the cpufreq driver and thus do not know what the current operating
0087  * point is. Fortunately, we also do not have any hardware supporting anything
0088  * but operating point 0 at the moment, thus we just peek that value directly
0089  * from the SDB partition. If we ever end up with actually slewing the system
0090  * clock and thus changing operating points, we'll have to find a way to
0091  * communicate with the CPU freq driver;
0092  */
0093 
0094 #include <linux/types.h>
0095 #include <linux/errno.h>
0096 #include <linux/kernel.h>
0097 #include <linux/delay.h>
0098 #include <linux/slab.h>
0099 #include <linux/init.h>
0100 #include <linux/spinlock.h>
0101 #include <linux/wait.h>
0102 #include <linux/kmod.h>
0103 #include <linux/device.h>
0104 #include <linux/platform_device.h>
0105 #include <linux/of.h>
0106 
0107 #include <asm/machdep.h>
0108 #include <asm/io.h>
0109 #include <asm/sections.h>
0110 #include <asm/smu.h>
0111 
0112 #include "windfarm.h"
0113 #include "windfarm_pid.h"
0114 
0115 #define VERSION "0.4"
0116 
0117 #undef DEBUG
0118 
0119 #ifdef DEBUG
0120 #define DBG(args...)    printk(args)
0121 #else
0122 #define DBG(args...)    do { } while(0)
0123 #endif
0124 
0125 /* define this to force CPU overtemp to 74 degree, useful for testing
0126  * the overtemp code
0127  */
0128 #undef HACKED_OVERTEMP
0129 
0130 static int wf_smu_mach_model;   /* machine model id */
0131 
0132 /* Controls & sensors */
0133 static struct wf_sensor *sensor_cpu_power;
0134 static struct wf_sensor *sensor_cpu_temp;
0135 static struct wf_sensor *sensor_hd_temp;
0136 static struct wf_control *fan_cpu_main;
0137 static struct wf_control *fan_hd;
0138 static struct wf_control *fan_system;
0139 static struct wf_control *cpufreq_clamp;
0140 
0141 /* Set to kick the control loop into life */
0142 static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok;
0143 static bool wf_smu_started;
0144 
0145 /* Failure handling.. could be nicer */
0146 #define FAILURE_FAN     0x01
0147 #define FAILURE_SENSOR      0x02
0148 #define FAILURE_OVERTEMP    0x04
0149 
0150 static unsigned int wf_smu_failure_state;
0151 static int wf_smu_readjust, wf_smu_skipping;
0152 static bool wf_smu_overtemp;
0153 
0154 /*
0155  * ****** System Fans Control Loop ******
0156  *
0157  */
0158 
0159 /* Parameters for the System Fans control loop. Parameters
0160  * not in this table such as interval, history size, ...
0161  * are common to all versions and thus hard coded for now.
0162  */
0163 struct wf_smu_sys_fans_param {
0164     int model_id;
0165     s32 itarget;
0166     s32 gd, gp, gr;
0167 
0168     s16 offset0;
0169     u16 scale0;
0170     s16 offset1;
0171     u16 scale1;
0172 };
0173 
0174 #define WF_SMU_SYS_FANS_INTERVAL    5
0175 #define WF_SMU_SYS_FANS_HISTORY_SIZE    2
0176 
0177 /* State data used by the system fans control loop
0178  */
0179 struct wf_smu_sys_fans_state {
0180     int         ticks;
0181     s32         sys_setpoint;
0182     s32         hd_setpoint;
0183     s16         offset0;
0184     u16         scale0;
0185     s16         offset1;
0186     u16         scale1;
0187     struct wf_pid_state pid;
0188 };
0189 
0190 /*
0191  * Configs for SMU System Fan control loop
0192  */
0193 static struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = {
0194     /* Model ID 2 */
0195     {
0196         .model_id   = 2,
0197         .itarget    = 0x3a0000,
0198         .gd     = 0x15400000,
0199         .gp     = 0x00200000,
0200         .gr     = 0x000002fd,
0201         .offset0    = 0xff38,
0202         .scale0     = 0x0ccd,
0203         .offset1    = 0x0208,
0204         .scale1     = 0x07ae,
0205     },
0206     /* Model ID 3 */
0207     {
0208         .model_id   = 3,
0209         .itarget    = 0x350000,
0210         .gd     = 0x08e00000,
0211         .gp     = 0x00566666,
0212         .gr     = 0x0000072b,
0213         .offset0    = 0xff38,
0214         .scale0     = 0x0ccd,
0215         .offset1    = 0x0000,
0216         .scale1     = 0x0000,
0217     },
0218     /* Model ID 5 */
0219     {
0220         .model_id   = 5,
0221         .itarget    = 0x3a0000,
0222         .gd     = 0x15400000,
0223         .gp     = 0x00233333,
0224         .gr     = 0x000002fd,
0225         .offset0    = 0x0000,
0226         .scale0     = 0x1000,
0227         .offset1    = 0x0091,
0228         .scale1     = 0x0bae,
0229     },
0230 };
0231 #define WF_SMU_SYS_FANS_NUM_CONFIGS ARRAY_SIZE(wf_smu_sys_all_params)
0232 
0233 static struct wf_smu_sys_fans_state *wf_smu_sys_fans;
0234 
0235 /*
0236  * ****** CPU Fans Control Loop ******
0237  *
0238  */
0239 
0240 
0241 #define WF_SMU_CPU_FANS_INTERVAL    1
0242 #define WF_SMU_CPU_FANS_MAX_HISTORY 16
0243 #define WF_SMU_CPU_FANS_SIBLING_SCALE   0x00001000
0244 #define WF_SMU_CPU_FANS_SIBLING_OFFSET  0xfffffb50
0245 
0246 /* State data used by the cpu fans control loop
0247  */
0248 struct wf_smu_cpu_fans_state {
0249     int         ticks;
0250     s32         cpu_setpoint;
0251     s32         scale;
0252     s32         offset;
0253     struct wf_cpu_pid_state pid;
0254 };
0255 
0256 static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;
0257 
0258 
0259 
0260 /*
0261  * ***** Implementation *****
0262  *
0263  */
0264 
0265 static void wf_smu_create_sys_fans(void)
0266 {
0267     struct wf_smu_sys_fans_param *param = NULL;
0268     struct wf_pid_param pid_param;
0269     int i;
0270 
0271     /* First, locate the params for this model */
0272     for (i = 0; i < WF_SMU_SYS_FANS_NUM_CONFIGS; i++)
0273         if (wf_smu_sys_all_params[i].model_id == wf_smu_mach_model) {
0274             param = &wf_smu_sys_all_params[i];
0275             break;
0276         }
0277 
0278     /* No params found, put fans to max */
0279     if (param == NULL) {
0280         printk(KERN_WARNING "windfarm: System fan config not found "
0281                "for this machine model, max fan speed\n");
0282         goto fail;
0283     }
0284 
0285     /* Alloc & initialize state */
0286     wf_smu_sys_fans = kmalloc(sizeof(struct wf_smu_sys_fans_state),
0287                   GFP_KERNEL);
0288     if (wf_smu_sys_fans == NULL) {
0289         printk(KERN_WARNING "windfarm: Memory allocation error"
0290                " max fan speed\n");
0291         goto fail;
0292     }
0293     wf_smu_sys_fans->ticks = 1;
0294     wf_smu_sys_fans->scale0 = param->scale0;
0295     wf_smu_sys_fans->offset0 = param->offset0;
0296     wf_smu_sys_fans->scale1 = param->scale1;
0297     wf_smu_sys_fans->offset1 = param->offset1;
0298 
0299     /* Fill PID params */
0300     pid_param.gd = param->gd;
0301     pid_param.gp = param->gp;
0302     pid_param.gr = param->gr;
0303     pid_param.interval = WF_SMU_SYS_FANS_INTERVAL;
0304     pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE;
0305     pid_param.itarget = param->itarget;
0306     pid_param.min = wf_control_get_min(fan_system);
0307     pid_param.max = wf_control_get_max(fan_system);
0308     if (fan_hd) {
0309         pid_param.min =
0310             max(pid_param.min, wf_control_get_min(fan_hd));
0311         pid_param.max =
0312             min(pid_param.max, wf_control_get_max(fan_hd));
0313     }
0314     wf_pid_init(&wf_smu_sys_fans->pid, &pid_param);
0315 
0316     DBG("wf: System Fan control initialized.\n");
0317     DBG("    itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
0318         FIX32TOPRINT(pid_param.itarget), pid_param.min, pid_param.max);
0319     return;
0320 
0321  fail:
0322 
0323     if (fan_system)
0324         wf_control_set_max(fan_system);
0325     if (fan_hd)
0326         wf_control_set_max(fan_hd);
0327 }
0328 
0329 static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)
0330 {
0331     s32 new_setpoint, temp, scaled, cputarget;
0332     int rc;
0333 
0334     if (--st->ticks != 0) {
0335         if (wf_smu_readjust)
0336             goto readjust;
0337         return;
0338     }
0339     st->ticks = WF_SMU_SYS_FANS_INTERVAL;
0340 
0341     rc = wf_sensor_get(sensor_hd_temp, &temp);
0342     if (rc) {
0343         printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
0344                rc);
0345         wf_smu_failure_state |= FAILURE_SENSOR;
0346         return;
0347     }
0348 
0349     DBG("wf_smu: System Fans tick ! HD temp: %d.%03d\n",
0350         FIX32TOPRINT(temp));
0351 
0352     if (temp > (st->pid.param.itarget + 0x50000))
0353         wf_smu_failure_state |= FAILURE_OVERTEMP;
0354 
0355     new_setpoint = wf_pid_run(&st->pid, temp);
0356 
0357     DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
0358 
0359     scaled = ((((s64)new_setpoint) * (s64)st->scale0) >> 12) + st->offset0;
0360 
0361     DBG("wf_smu: scaled setpoint: %d RPM\n", (int)scaled);
0362 
0363     cputarget = wf_smu_cpu_fans ? wf_smu_cpu_fans->pid.target : 0;
0364     cputarget = ((((s64)cputarget) * (s64)st->scale1) >> 12) + st->offset1;
0365     scaled = max(scaled, cputarget);
0366     scaled = max(scaled, st->pid.param.min);
0367     scaled = min(scaled, st->pid.param.max);
0368 
0369     DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)scaled);
0370 
0371     if (st->sys_setpoint == scaled && new_setpoint == st->hd_setpoint)
0372         return;
0373     st->sys_setpoint = scaled;
0374     st->hd_setpoint = new_setpoint;
0375  readjust:
0376     if (fan_system && wf_smu_failure_state == 0) {
0377         rc = wf_control_set(fan_system, st->sys_setpoint);
0378         if (rc) {
0379             printk(KERN_WARNING "windfarm: Sys fan error %d\n",
0380                    rc);
0381             wf_smu_failure_state |= FAILURE_FAN;
0382         }
0383     }
0384     if (fan_hd && wf_smu_failure_state == 0) {
0385         rc = wf_control_set(fan_hd, st->hd_setpoint);
0386         if (rc) {
0387             printk(KERN_WARNING "windfarm: HD fan error %d\n",
0388                    rc);
0389             wf_smu_failure_state |= FAILURE_FAN;
0390         }
0391     }
0392 }
0393 
0394 static void wf_smu_create_cpu_fans(void)
0395 {
0396     struct wf_cpu_pid_param pid_param;
0397     const struct smu_sdbp_header *hdr;
0398     struct smu_sdbp_cpupiddata *piddata;
0399     struct smu_sdbp_fvt *fvt;
0400     s32 tmax, tdelta, maxpow, powadj;
0401 
0402     /* First, locate the PID params in SMU SBD */
0403     hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
0404     if (hdr == 0) {
0405         printk(KERN_WARNING "windfarm: CPU PID fan config not found "
0406                "max fan speed\n");
0407         goto fail;
0408     }
0409     piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
0410 
0411     /* Get the FVT params for operating point 0 (the only supported one
0412      * for now) in order to get tmax
0413      */
0414     hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
0415     if (hdr) {
0416         fvt = (struct smu_sdbp_fvt *)&hdr[1];
0417         tmax = ((s32)fvt->maxtemp) << 16;
0418     } else
0419         tmax = 0x5e0000; /* 94 degree default */
0420 
0421     /* Alloc & initialize state */
0422     wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),
0423                   GFP_KERNEL);
0424     if (wf_smu_cpu_fans == NULL)
0425         goto fail;
0426         wf_smu_cpu_fans->ticks = 1;
0427 
0428     wf_smu_cpu_fans->scale = WF_SMU_CPU_FANS_SIBLING_SCALE;
0429     wf_smu_cpu_fans->offset = WF_SMU_CPU_FANS_SIBLING_OFFSET;
0430 
0431     /* Fill PID params */
0432     pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;
0433     pid_param.history_len = piddata->history_len;
0434     if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
0435         printk(KERN_WARNING "windfarm: History size overflow on "
0436                "CPU control loop (%d)\n", piddata->history_len);
0437         pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
0438     }
0439     pid_param.gd = piddata->gd;
0440     pid_param.gp = piddata->gp;
0441     pid_param.gr = piddata->gr / pid_param.history_len;
0442 
0443     tdelta = ((s32)piddata->target_temp_delta) << 16;
0444     maxpow = ((s32)piddata->max_power) << 16;
0445     powadj = ((s32)piddata->power_adj) << 16;
0446 
0447     pid_param.tmax = tmax;
0448     pid_param.ttarget = tmax - tdelta;
0449     pid_param.pmaxadj = maxpow - powadj;
0450 
0451     pid_param.min = wf_control_get_min(fan_cpu_main);
0452     pid_param.max = wf_control_get_max(fan_cpu_main);
0453 
0454     wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
0455 
0456     DBG("wf: CPU Fan control initialized.\n");
0457     DBG("    ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",
0458         FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
0459         pid_param.min, pid_param.max);
0460 
0461     return;
0462 
0463  fail:
0464     printk(KERN_WARNING "windfarm: CPU fan config not found\n"
0465            "for this machine model, max fan speed\n");
0466 
0467     if (cpufreq_clamp)
0468         wf_control_set_max(cpufreq_clamp);
0469     if (fan_cpu_main)
0470         wf_control_set_max(fan_cpu_main);
0471 }
0472 
0473 static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
0474 {
0475     s32 new_setpoint, temp, power, systarget;
0476     int rc;
0477 
0478     if (--st->ticks != 0) {
0479         if (wf_smu_readjust)
0480             goto readjust;
0481         return;
0482     }
0483     st->ticks = WF_SMU_CPU_FANS_INTERVAL;
0484 
0485     rc = wf_sensor_get(sensor_cpu_temp, &temp);
0486     if (rc) {
0487         printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
0488                rc);
0489         wf_smu_failure_state |= FAILURE_SENSOR;
0490         return;
0491     }
0492 
0493     rc = wf_sensor_get(sensor_cpu_power, &power);
0494     if (rc) {
0495         printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
0496                rc);
0497         wf_smu_failure_state |= FAILURE_SENSOR;
0498         return;
0499     }
0500 
0501     DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",
0502         FIX32TOPRINT(temp), FIX32TOPRINT(power));
0503 
0504 #ifdef HACKED_OVERTEMP
0505     if (temp > 0x4a0000)
0506         wf_smu_failure_state |= FAILURE_OVERTEMP;
0507 #else
0508     if (temp > st->pid.param.tmax)
0509         wf_smu_failure_state |= FAILURE_OVERTEMP;
0510 #endif
0511     new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
0512 
0513     DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
0514 
0515     systarget = wf_smu_sys_fans ? wf_smu_sys_fans->pid.target : 0;
0516     systarget = ((((s64)systarget) * (s64)st->scale) >> 12)
0517         + st->offset;
0518     new_setpoint = max(new_setpoint, systarget);
0519     new_setpoint = max(new_setpoint, st->pid.param.min);
0520     new_setpoint = min(new_setpoint, st->pid.param.max);
0521 
0522     DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)new_setpoint);
0523 
0524     if (st->cpu_setpoint == new_setpoint)
0525         return;
0526     st->cpu_setpoint = new_setpoint;
0527  readjust:
0528     if (fan_cpu_main && wf_smu_failure_state == 0) {
0529         rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
0530         if (rc) {
0531             printk(KERN_WARNING "windfarm: CPU main fan"
0532                    " error %d\n", rc);
0533             wf_smu_failure_state |= FAILURE_FAN;
0534         }
0535     }
0536 }
0537 
0538 /*
0539  * ****** Setup / Init / Misc ... ******
0540  *
0541  */
0542 
0543 static void wf_smu_tick(void)
0544 {
0545     unsigned int last_failure = wf_smu_failure_state;
0546     unsigned int new_failure;
0547 
0548     if (!wf_smu_started) {
0549         DBG("wf: creating control loops !\n");
0550         wf_smu_create_sys_fans();
0551         wf_smu_create_cpu_fans();
0552         wf_smu_started = true;
0553     }
0554 
0555     /* Skipping ticks */
0556     if (wf_smu_skipping && --wf_smu_skipping)
0557         return;
0558 
0559     wf_smu_failure_state = 0;
0560     if (wf_smu_sys_fans)
0561         wf_smu_sys_fans_tick(wf_smu_sys_fans);
0562     if (wf_smu_cpu_fans)
0563         wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
0564 
0565     wf_smu_readjust = 0;
0566     new_failure = wf_smu_failure_state & ~last_failure;
0567 
0568     /* If entering failure mode, clamp cpufreq and ramp all
0569      * fans to full speed.
0570      */
0571     if (wf_smu_failure_state && !last_failure) {
0572         if (cpufreq_clamp)
0573             wf_control_set_max(cpufreq_clamp);
0574         if (fan_system)
0575             wf_control_set_max(fan_system);
0576         if (fan_cpu_main)
0577             wf_control_set_max(fan_cpu_main);
0578         if (fan_hd)
0579             wf_control_set_max(fan_hd);
0580     }
0581 
0582     /* If leaving failure mode, unclamp cpufreq and readjust
0583      * all fans on next iteration
0584      */
0585     if (!wf_smu_failure_state && last_failure) {
0586         if (cpufreq_clamp)
0587             wf_control_set_min(cpufreq_clamp);
0588         wf_smu_readjust = 1;
0589     }
0590 
0591     /* Overtemp condition detected, notify and start skipping a couple
0592      * ticks to let the temperature go down
0593      */
0594     if (new_failure & FAILURE_OVERTEMP) {
0595         wf_set_overtemp();
0596         wf_smu_skipping = 2;
0597         wf_smu_overtemp = true;
0598     }
0599 
0600     /* We only clear the overtemp condition if overtemp is cleared
0601      * _and_ no other failure is present. Since a sensor error will
0602      * clear the overtemp condition (can't measure temperature) at
0603      * the control loop levels, but we don't want to keep it clear
0604      * here in this case
0605      */
0606     if (!wf_smu_failure_state && wf_smu_overtemp) {
0607         wf_clear_overtemp();
0608         wf_smu_overtemp = false;
0609     }
0610 }
0611 
0612 static void wf_smu_new_control(struct wf_control *ct)
0613 {
0614     if (wf_smu_all_controls_ok)
0615         return;
0616 
0617     if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) {
0618         if (wf_get_control(ct) == 0)
0619             fan_cpu_main = ct;
0620     }
0621 
0622     if (fan_system == NULL && !strcmp(ct->name, "system-fan")) {
0623         if (wf_get_control(ct) == 0)
0624             fan_system = ct;
0625     }
0626 
0627     if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
0628         if (wf_get_control(ct) == 0)
0629             cpufreq_clamp = ct;
0630     }
0631 
0632     /* Darwin property list says the HD fan is only for model ID
0633      * 0, 1, 2 and 3
0634      */
0635 
0636     if (wf_smu_mach_model > 3) {
0637         if (fan_system && fan_cpu_main && cpufreq_clamp)
0638             wf_smu_all_controls_ok = 1;
0639         return;
0640     }
0641 
0642     if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {
0643         if (wf_get_control(ct) == 0)
0644             fan_hd = ct;
0645     }
0646 
0647     if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp)
0648         wf_smu_all_controls_ok = 1;
0649 }
0650 
0651 static void wf_smu_new_sensor(struct wf_sensor *sr)
0652 {
0653     if (wf_smu_all_sensors_ok)
0654         return;
0655 
0656     if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {
0657         if (wf_get_sensor(sr) == 0)
0658             sensor_cpu_power = sr;
0659     }
0660 
0661     if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {
0662         if (wf_get_sensor(sr) == 0)
0663             sensor_cpu_temp = sr;
0664     }
0665 
0666     if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {
0667         if (wf_get_sensor(sr) == 0)
0668             sensor_hd_temp = sr;
0669     }
0670 
0671     if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp)
0672         wf_smu_all_sensors_ok = 1;
0673 }
0674 
0675 
0676 static int wf_smu_notify(struct notifier_block *self,
0677                    unsigned long event, void *data)
0678 {
0679     switch(event) {
0680     case WF_EVENT_NEW_CONTROL:
0681         DBG("wf: new control %s detected\n",
0682             ((struct wf_control *)data)->name);
0683         wf_smu_new_control(data);
0684         wf_smu_readjust = 1;
0685         break;
0686     case WF_EVENT_NEW_SENSOR:
0687         DBG("wf: new sensor %s detected\n",
0688             ((struct wf_sensor *)data)->name);
0689         wf_smu_new_sensor(data);
0690         break;
0691     case WF_EVENT_TICK:
0692         if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)
0693             wf_smu_tick();
0694     }
0695 
0696     return 0;
0697 }
0698 
0699 static struct notifier_block wf_smu_events = {
0700     .notifier_call  = wf_smu_notify,
0701 };
0702 
0703 static int wf_init_pm(void)
0704 {
0705     const struct smu_sdbp_header *hdr;
0706 
0707     hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
0708     if (hdr != 0) {
0709         struct smu_sdbp_sensortree *st =
0710             (struct smu_sdbp_sensortree *)&hdr[1];
0711         wf_smu_mach_model = st->model_id;
0712     }
0713 
0714     printk(KERN_INFO "windfarm: Initializing for iMacG5 model ID %d\n",
0715            wf_smu_mach_model);
0716 
0717     return 0;
0718 }
0719 
0720 static int wf_smu_probe(struct platform_device *ddev)
0721 {
0722     wf_register_client(&wf_smu_events);
0723 
0724     return 0;
0725 }
0726 
0727 static int wf_smu_remove(struct platform_device *ddev)
0728 {
0729     wf_unregister_client(&wf_smu_events);
0730 
0731     /* XXX We don't have yet a guarantee that our callback isn't
0732      * in progress when returning from wf_unregister_client, so
0733      * we add an arbitrary delay. I'll have to fix that in the core
0734      */
0735     msleep(1000);
0736 
0737     /* Release all sensors */
0738     /* One more crappy race: I don't think we have any guarantee here
0739      * that the attribute callback won't race with the sensor beeing
0740      * disposed of, and I'm not 100% certain what best way to deal
0741      * with that except by adding locks all over... I'll do that
0742      * eventually but heh, who ever rmmod this module anyway ?
0743      */
0744     if (sensor_cpu_power)
0745         wf_put_sensor(sensor_cpu_power);
0746     if (sensor_cpu_temp)
0747         wf_put_sensor(sensor_cpu_temp);
0748     if (sensor_hd_temp)
0749         wf_put_sensor(sensor_hd_temp);
0750 
0751     /* Release all controls */
0752     if (fan_cpu_main)
0753         wf_put_control(fan_cpu_main);
0754     if (fan_hd)
0755         wf_put_control(fan_hd);
0756     if (fan_system)
0757         wf_put_control(fan_system);
0758     if (cpufreq_clamp)
0759         wf_put_control(cpufreq_clamp);
0760 
0761     /* Destroy control loops state structures */
0762     kfree(wf_smu_sys_fans);
0763     kfree(wf_smu_cpu_fans);
0764 
0765     return 0;
0766 }
0767 
0768 static struct platform_driver wf_smu_driver = {
0769         .probe = wf_smu_probe,
0770         .remove = wf_smu_remove,
0771     .driver = {
0772         .name = "windfarm",
0773     },
0774 };
0775 
0776 
0777 static int __init wf_smu_init(void)
0778 {
0779     int rc = -ENODEV;
0780 
0781     if (of_machine_is_compatible("PowerMac8,1") ||
0782         of_machine_is_compatible("PowerMac8,2"))
0783         rc = wf_init_pm();
0784 
0785     if (rc == 0) {
0786 #ifdef MODULE
0787         request_module("windfarm_smu_controls");
0788         request_module("windfarm_smu_sensors");
0789         request_module("windfarm_lm75_sensor");
0790         request_module("windfarm_cpufreq_clamp");
0791 
0792 #endif /* MODULE */
0793         platform_driver_register(&wf_smu_driver);
0794     }
0795 
0796     return rc;
0797 }
0798 
0799 static void __exit wf_smu_exit(void)
0800 {
0801 
0802     platform_driver_unregister(&wf_smu_driver);
0803 }
0804 
0805 
0806 module_init(wf_smu_init);
0807 module_exit(wf_smu_exit);
0808 
0809 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
0810 MODULE_DESCRIPTION("Thermal control logic for iMac G5");
0811 MODULE_LICENSE("GPL");
0812 MODULE_ALIAS("platform:windfarm");