^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Windfarm PowerMac thermal control. iMac G5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * <benh@kernel.crashing.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * The algorithm used is the PID control algorithm, used the same
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * way the published Darwin code does, using the same values that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * are present in the Darwin 8.2 snapshot property lists (note however
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * that none of the code has been re-used, it's a complete re-implementation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * The various control loops found in Darwin config file are:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * PowerMac8,1 and PowerMac8,2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * ===========================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * System Fans control loop. Different based on models. In addition to the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * usual PID algorithm, the control loop gets 2 additional pairs of linear
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * scaling factors (scale/offsets) expressed as 4.12 fixed point values
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * signed offset, unsigned scale)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * The targets are modified such as:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * - the linked control (second control) gets the target value as-is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * (typically the drive fan)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * - the main control (first control) gets the target value scaled with
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * the first pair of factors, and is then modified as below
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * - the value of the target of the CPU Fan control loop is retrieved,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * scaled with the second pair of factors, and the max of that and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * the scaled target is applied to the main control.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * # model_id: 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * controls : system-fan, drive-bay-fan
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * sensors : hd-temp
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * PID params : G_d = 0x15400000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * G_p = 0x00200000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * G_r = 0x000002fd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * History = 2 entries
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * Input target = 0x3a0000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * Interval = 5s
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) * linear-factors : offset = 0xff38 scale = 0x0ccd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * offset = 0x0208 scale = 0x07ae
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) * # model_id: 3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * controls : system-fan, drive-bay-fan
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) * sensors : hd-temp
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) * PID params : G_d = 0x08e00000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) * G_p = 0x00566666
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) * G_r = 0x0000072b
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) * History = 2 entries
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) * Input target = 0x350000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) * Interval = 5s
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) * linear-factors : offset = 0xff38 scale = 0x0ccd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) * offset = 0x0000 scale = 0x0000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) * # model_id: 5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) * controls : system-fan
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) * sensors : hd-temp
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) * PID params : G_d = 0x15400000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) * G_p = 0x00233333
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) * G_r = 0x000002fd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) * History = 2 entries
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * Input target = 0x3a0000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * Interval = 5s
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * linear-factors : offset = 0x0000 scale = 0x1000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * offset = 0x0091 scale = 0x0bae
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) * CPU Fan control loop. The loop is identical for all models. it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) * has an additional pair of scaling factor. This is used to scale the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) * systems fan control loop target result (the one before it gets scaled
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * by the System Fans control loop itself). Then, the max value of the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * calculated target value and system fan value is sent to the fans
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) * controls : cpu-fan
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) * sensors : cpu-temp cpu-power
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) * PID params : From SMU sdb partition
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) * linear-factors : offset = 0xfb50 scale = 0x1000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) * CPU Slew control loop. Not implemented. The cpufreq driver in linux is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) * completely separate for now, though we could find a way to link it, either
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) * as a client reacting to overtemp notifications, or directling monitoring
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) * the CPU temperature
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) * WARNING ! The CPU control loop requires the CPU tmax for the current
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) * operating point. However, we currently are completely separated from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) * the cpufreq driver and thus do not know what the current operating
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) * point is. Fortunately, we also do not have any hardware supporting anything
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) * but operating point 0 at the moment, thus we just peek that value directly
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) * from the SDB partition. If we ever end up with actually slewing the system
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * clock and thus changing operating points, we'll have to find a way to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * communicate with the CPU freq driver;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) #include <linux/spinlock.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) #include <linux/wait.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) #include <linux/kmod.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) #include <linux/device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) #include <asm/prom.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) #include <asm/machdep.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) #include <asm/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) #include <asm/sections.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) #include <asm/smu.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) #include "windfarm.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) #include "windfarm_pid.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) #define VERSION "0.4"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) #undef DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) #ifdef DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) #define DBG(args...) printk(args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) #define DBG(args...) do { } while(0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) /* define this to force CPU overtemp to 74 degree, useful for testing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) * the overtemp code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) #undef HACKED_OVERTEMP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) static int wf_smu_mach_model; /* machine model id */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) /* Controls & sensors */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) static struct wf_sensor *sensor_cpu_power;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) static struct wf_sensor *sensor_cpu_temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) static struct wf_sensor *sensor_hd_temp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) static struct wf_control *fan_cpu_main;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) static struct wf_control *fan_hd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) static struct wf_control *fan_system;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) static struct wf_control *cpufreq_clamp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) /* Set to kick the control loop into life */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) static bool wf_smu_started;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) /* Failure handling.. could be nicer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) #define FAILURE_FAN 0x01
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) #define FAILURE_SENSOR 0x02
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) #define FAILURE_OVERTEMP 0x04
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) static unsigned int wf_smu_failure_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) static int wf_smu_readjust, wf_smu_skipping;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) static bool wf_smu_overtemp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) * ****** System Fans Control Loop ******
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) /* Parameters for the System Fans control loop. Parameters
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) * not in this table such as interval, history size, ...
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) * are common to all versions and thus hard coded for now.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) struct wf_smu_sys_fans_param {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) int model_id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) s32 itarget;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) s32 gd, gp, gr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) s16 offset0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) u16 scale0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) s16 offset1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) u16 scale1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) #define WF_SMU_SYS_FANS_INTERVAL 5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) #define WF_SMU_SYS_FANS_HISTORY_SIZE 2
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) /* State data used by the system fans control loop
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) struct wf_smu_sys_fans_state {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) int ticks;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) s32 sys_setpoint;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) s32 hd_setpoint;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) s16 offset0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) u16 scale0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) s16 offset1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) u16 scale1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) struct wf_pid_state pid;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) * Configs for SMU System Fan control loop
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) static struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) /* Model ID 2 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) .model_id = 2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) .itarget = 0x3a0000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) .gd = 0x15400000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) .gp = 0x00200000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) .gr = 0x000002fd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) .offset0 = 0xff38,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) .scale0 = 0x0ccd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) .offset1 = 0x0208,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) .scale1 = 0x07ae,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) /* Model ID 3 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) .model_id = 3,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) .itarget = 0x350000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) .gd = 0x08e00000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) .gp = 0x00566666,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) .gr = 0x0000072b,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) .offset0 = 0xff38,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) .scale0 = 0x0ccd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) .offset1 = 0x0000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) .scale1 = 0x0000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) /* Model ID 5 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) .model_id = 5,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) .itarget = 0x3a0000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) .gd = 0x15400000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) .gp = 0x00233333,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) .gr = 0x000002fd,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) .offset0 = 0x0000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) .scale0 = 0x1000,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) .offset1 = 0x0091,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) .scale1 = 0x0bae,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) #define WF_SMU_SYS_FANS_NUM_CONFIGS ARRAY_SIZE(wf_smu_sys_all_params)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) static struct wf_smu_sys_fans_state *wf_smu_sys_fans;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) * ****** CPU Fans Control Loop ******
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) #define WF_SMU_CPU_FANS_INTERVAL 1
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) #define WF_SMU_CPU_FANS_MAX_HISTORY 16
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) #define WF_SMU_CPU_FANS_SIBLING_SCALE 0x00001000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) #define WF_SMU_CPU_FANS_SIBLING_OFFSET 0xfffffb50
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) /* State data used by the cpu fans control loop
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) struct wf_smu_cpu_fans_state {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) int ticks;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) s32 cpu_setpoint;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) s32 scale;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) s32 offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) struct wf_cpu_pid_state pid;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) * ***** Implementation *****
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) static void wf_smu_create_sys_fans(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) struct wf_smu_sys_fans_param *param = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) struct wf_pid_param pid_param;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) /* First, locate the params for this model */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) for (i = 0; i < WF_SMU_SYS_FANS_NUM_CONFIGS; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) if (wf_smu_sys_all_params[i].model_id == wf_smu_mach_model) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) param = &wf_smu_sys_all_params[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) /* No params found, put fans to max */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) if (param == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) printk(KERN_WARNING "windfarm: System fan config not found "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) "for this machine model, max fan speed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) /* Alloc & initialize state */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) wf_smu_sys_fans = kmalloc(sizeof(struct wf_smu_sys_fans_state),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) if (wf_smu_sys_fans == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) printk(KERN_WARNING "windfarm: Memory allocation error"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) " max fan speed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) wf_smu_sys_fans->ticks = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) wf_smu_sys_fans->scale0 = param->scale0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) wf_smu_sys_fans->offset0 = param->offset0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) wf_smu_sys_fans->scale1 = param->scale1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) wf_smu_sys_fans->offset1 = param->offset1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) /* Fill PID params */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) pid_param.gd = param->gd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) pid_param.gp = param->gp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) pid_param.gr = param->gr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) pid_param.interval = WF_SMU_SYS_FANS_INTERVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) pid_param.itarget = param->itarget;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) pid_param.min = wf_control_get_min(fan_system);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) pid_param.max = wf_control_get_max(fan_system);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) if (fan_hd) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) pid_param.min =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) max(pid_param.min, wf_control_get_min(fan_hd));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) pid_param.max =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) min(pid_param.max, wf_control_get_max(fan_hd));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) wf_pid_init(&wf_smu_sys_fans->pid, &pid_param);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) DBG("wf: System Fan control initialized.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) FIX32TOPRINT(pid_param.itarget), pid_param.min, pid_param.max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) fail:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) if (fan_system)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) wf_control_set_max(fan_system);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) if (fan_hd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) wf_control_set_max(fan_hd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) s32 new_setpoint, temp, scaled, cputarget;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) if (--st->ticks != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) if (wf_smu_readjust)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) goto readjust;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) st->ticks = WF_SMU_SYS_FANS_INTERVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) rc = wf_sensor_get(sensor_hd_temp, &temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) wf_smu_failure_state |= FAILURE_SENSOR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) DBG("wf_smu: System Fans tick ! HD temp: %d.%03d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349) FIX32TOPRINT(temp));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) if (temp > (st->pid.param.itarget + 0x50000))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) wf_smu_failure_state |= FAILURE_OVERTEMP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) new_setpoint = wf_pid_run(&st->pid, temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) scaled = ((((s64)new_setpoint) * (s64)st->scale0) >> 12) + st->offset0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) DBG("wf_smu: scaled setpoint: %d RPM\n", (int)scaled);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) cputarget = wf_smu_cpu_fans ? wf_smu_cpu_fans->pid.target : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) cputarget = ((((s64)cputarget) * (s64)st->scale1) >> 12) + st->offset1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) scaled = max(scaled, cputarget);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) scaled = max(scaled, st->pid.param.min);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) scaled = min(scaled, st->pid.param.max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)scaled);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) if (st->sys_setpoint == scaled && new_setpoint == st->hd_setpoint)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) st->sys_setpoint = scaled;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) st->hd_setpoint = new_setpoint;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) readjust:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 375) if (fan_system && wf_smu_failure_state == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 376) rc = wf_control_set(fan_system, st->sys_setpoint);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 377) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 378) printk(KERN_WARNING "windfarm: Sys fan error %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 379) rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 380) wf_smu_failure_state |= FAILURE_FAN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 381) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 382) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 383) if (fan_hd && wf_smu_failure_state == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 384) rc = wf_control_set(fan_hd, st->hd_setpoint);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 385) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 386) printk(KERN_WARNING "windfarm: HD fan error %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 387) rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 388) wf_smu_failure_state |= FAILURE_FAN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 389) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 390) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 391) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 392)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 393) static void wf_smu_create_cpu_fans(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 394) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 395) struct wf_cpu_pid_param pid_param;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 396) const struct smu_sdbp_header *hdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 397) struct smu_sdbp_cpupiddata *piddata;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 398) struct smu_sdbp_fvt *fvt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 399) s32 tmax, tdelta, maxpow, powadj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 400)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 401) /* First, locate the PID params in SMU SBD */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 402) hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 403) if (hdr == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 404) printk(KERN_WARNING "windfarm: CPU PID fan config not found "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 405) "max fan speed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 406) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 407) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 408) piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 409)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 410) /* Get the FVT params for operating point 0 (the only supported one
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 411) * for now) in order to get tmax
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 412) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 413) hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 414) if (hdr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 415) fvt = (struct smu_sdbp_fvt *)&hdr[1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 416) tmax = ((s32)fvt->maxtemp) << 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 417) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 418) tmax = 0x5e0000; /* 94 degree default */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 419)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 420) /* Alloc & initialize state */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 421) wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 422) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 423) if (wf_smu_cpu_fans == NULL)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 424) goto fail;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 425) wf_smu_cpu_fans->ticks = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 426)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 427) wf_smu_cpu_fans->scale = WF_SMU_CPU_FANS_SIBLING_SCALE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 428) wf_smu_cpu_fans->offset = WF_SMU_CPU_FANS_SIBLING_OFFSET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 429)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 430) /* Fill PID params */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 431) pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 432) pid_param.history_len = piddata->history_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 433) if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 434) printk(KERN_WARNING "windfarm: History size overflow on "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 435) "CPU control loop (%d)\n", piddata->history_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 436) pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 437) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 438) pid_param.gd = piddata->gd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 439) pid_param.gp = piddata->gp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 440) pid_param.gr = piddata->gr / pid_param.history_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 441)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 442) tdelta = ((s32)piddata->target_temp_delta) << 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 443) maxpow = ((s32)piddata->max_power) << 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 444) powadj = ((s32)piddata->power_adj) << 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 445)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 446) pid_param.tmax = tmax;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 447) pid_param.ttarget = tmax - tdelta;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 448) pid_param.pmaxadj = maxpow - powadj;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 449)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 450) pid_param.min = wf_control_get_min(fan_cpu_main);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 451) pid_param.max = wf_control_get_max(fan_cpu_main);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 452)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 453) wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 454)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 455) DBG("wf: CPU Fan control initialized.\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 456) DBG(" ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 457) FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 458) pid_param.min, pid_param.max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 459)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 460) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 461)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 462) fail:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 463) printk(KERN_WARNING "windfarm: CPU fan config not found\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 464) "for this machine model, max fan speed\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 465)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 466) if (cpufreq_clamp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 467) wf_control_set_max(cpufreq_clamp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 468) if (fan_cpu_main)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 469) wf_control_set_max(fan_cpu_main);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 470) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 471)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 472) static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 473) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 474) s32 new_setpoint, temp, power, systarget;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 475) int rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 476)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 477) if (--st->ticks != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 478) if (wf_smu_readjust)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 479) goto readjust;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 480) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 481) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 482) st->ticks = WF_SMU_CPU_FANS_INTERVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 483)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 484) rc = wf_sensor_get(sensor_cpu_temp, &temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 485) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 486) printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 487) rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 488) wf_smu_failure_state |= FAILURE_SENSOR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 489) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 490) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 491)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 492) rc = wf_sensor_get(sensor_cpu_power, &power);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 493) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 494) printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 495) rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 496) wf_smu_failure_state |= FAILURE_SENSOR;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 497) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 498) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 499)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 500) DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 501) FIX32TOPRINT(temp), FIX32TOPRINT(power));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 502)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 503) #ifdef HACKED_OVERTEMP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 504) if (temp > 0x4a0000)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 505) wf_smu_failure_state |= FAILURE_OVERTEMP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 506) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 507) if (temp > st->pid.param.tmax)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 508) wf_smu_failure_state |= FAILURE_OVERTEMP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 509) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 510) new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 511)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 512) DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 513)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 514) systarget = wf_smu_sys_fans ? wf_smu_sys_fans->pid.target : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 515) systarget = ((((s64)systarget) * (s64)st->scale) >> 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 516) + st->offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 517) new_setpoint = max(new_setpoint, systarget);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 518) new_setpoint = max(new_setpoint, st->pid.param.min);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 519) new_setpoint = min(new_setpoint, st->pid.param.max);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 520)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 521) DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)new_setpoint);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 522)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 523) if (st->cpu_setpoint == new_setpoint)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 524) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 525) st->cpu_setpoint = new_setpoint;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 526) readjust:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 527) if (fan_cpu_main && wf_smu_failure_state == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 528) rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 529) if (rc) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 530) printk(KERN_WARNING "windfarm: CPU main fan"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 531) " error %d\n", rc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 532) wf_smu_failure_state |= FAILURE_FAN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 533) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 534) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 535) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 536)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 537) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 538) * ****** Setup / Init / Misc ... ******
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 539) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 540) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 541)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 542) static void wf_smu_tick(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 543) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 544) unsigned int last_failure = wf_smu_failure_state;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 545) unsigned int new_failure;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 546)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 547) if (!wf_smu_started) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 548) DBG("wf: creating control loops !\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 549) wf_smu_create_sys_fans();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 550) wf_smu_create_cpu_fans();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 551) wf_smu_started = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 552) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 553)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 554) /* Skipping ticks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 555) if (wf_smu_skipping && --wf_smu_skipping)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 556) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 557)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 558) wf_smu_failure_state = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 559) if (wf_smu_sys_fans)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 560) wf_smu_sys_fans_tick(wf_smu_sys_fans);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 561) if (wf_smu_cpu_fans)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 562) wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 563)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 564) wf_smu_readjust = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 565) new_failure = wf_smu_failure_state & ~last_failure;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 566)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 567) /* If entering failure mode, clamp cpufreq and ramp all
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 568) * fans to full speed.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 569) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 570) if (wf_smu_failure_state && !last_failure) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 571) if (cpufreq_clamp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 572) wf_control_set_max(cpufreq_clamp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 573) if (fan_system)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 574) wf_control_set_max(fan_system);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 575) if (fan_cpu_main)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 576) wf_control_set_max(fan_cpu_main);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 577) if (fan_hd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 578) wf_control_set_max(fan_hd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 579) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 580)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 581) /* If leaving failure mode, unclamp cpufreq and readjust
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 582) * all fans on next iteration
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 583) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 584) if (!wf_smu_failure_state && last_failure) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 585) if (cpufreq_clamp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 586) wf_control_set_min(cpufreq_clamp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 587) wf_smu_readjust = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 588) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 589)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 590) /* Overtemp condition detected, notify and start skipping a couple
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 591) * ticks to let the temperature go down
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 592) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 593) if (new_failure & FAILURE_OVERTEMP) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 594) wf_set_overtemp();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 595) wf_smu_skipping = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 596) wf_smu_overtemp = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 597) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 598)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 599) /* We only clear the overtemp condition if overtemp is cleared
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 600) * _and_ no other failure is present. Since a sensor error will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 601) * clear the overtemp condition (can't measure temperature) at
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 602) * the control loop levels, but we don't want to keep it clear
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 603) * here in this case
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 604) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 605) if (!wf_smu_failure_state && wf_smu_overtemp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 606) wf_clear_overtemp();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 607) wf_smu_overtemp = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 608) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 609) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 610)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 611) static void wf_smu_new_control(struct wf_control *ct)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 612) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 613) if (wf_smu_all_controls_ok)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 614) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 615)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 616) if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 617) if (wf_get_control(ct) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 618) fan_cpu_main = ct;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 619) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 620)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 621) if (fan_system == NULL && !strcmp(ct->name, "system-fan")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 622) if (wf_get_control(ct) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 623) fan_system = ct;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 624) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 625)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 626) if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 627) if (wf_get_control(ct) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 628) cpufreq_clamp = ct;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 629) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 630)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 631) /* Darwin property list says the HD fan is only for model ID
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 632) * 0, 1, 2 and 3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 633) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 634)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 635) if (wf_smu_mach_model > 3) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 636) if (fan_system && fan_cpu_main && cpufreq_clamp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 637) wf_smu_all_controls_ok = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 638) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 639) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 640)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 641) if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 642) if (wf_get_control(ct) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 643) fan_hd = ct;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 644) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 645)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 646) if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 647) wf_smu_all_controls_ok = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 648) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 649)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 650) static void wf_smu_new_sensor(struct wf_sensor *sr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 651) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 652) if (wf_smu_all_sensors_ok)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 653) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 654)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 655) if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 656) if (wf_get_sensor(sr) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 657) sensor_cpu_power = sr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 658) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 659)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 660) if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 661) if (wf_get_sensor(sr) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 662) sensor_cpu_temp = sr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 663) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 664)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 665) if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 666) if (wf_get_sensor(sr) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 667) sensor_hd_temp = sr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 668) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 669)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 670) if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 671) wf_smu_all_sensors_ok = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 672) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 673)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 674)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 675) static int wf_smu_notify(struct notifier_block *self,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 676) unsigned long event, void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 677) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 678) switch(event) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 679) case WF_EVENT_NEW_CONTROL:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 680) DBG("wf: new control %s detected\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 681) ((struct wf_control *)data)->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 682) wf_smu_new_control(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 683) wf_smu_readjust = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 684) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 685) case WF_EVENT_NEW_SENSOR:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 686) DBG("wf: new sensor %s detected\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 687) ((struct wf_sensor *)data)->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 688) wf_smu_new_sensor(data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 689) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 690) case WF_EVENT_TICK:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 691) if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 692) wf_smu_tick();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 693) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 694)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 695) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 696) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 697)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 698) static struct notifier_block wf_smu_events = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 699) .notifier_call = wf_smu_notify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 700) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 701)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 702) static int wf_init_pm(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 703) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 704) const struct smu_sdbp_header *hdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 705)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 706) hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 707) if (hdr != 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 708) struct smu_sdbp_sensortree *st =
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 709) (struct smu_sdbp_sensortree *)&hdr[1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 710) wf_smu_mach_model = st->model_id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 711) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 712)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 713) printk(KERN_INFO "windfarm: Initializing for iMacG5 model ID %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 714) wf_smu_mach_model);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 715)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 716) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 717) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 718)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 719) static int wf_smu_probe(struct platform_device *ddev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 720) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 721) wf_register_client(&wf_smu_events);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 722)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 723) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 724) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 725)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 726) static int wf_smu_remove(struct platform_device *ddev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 727) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 728) wf_unregister_client(&wf_smu_events);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 729)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 730) /* XXX We don't have yet a guarantee that our callback isn't
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 731) * in progress when returning from wf_unregister_client, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 732) * we add an arbitrary delay. I'll have to fix that in the core
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 733) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 734) msleep(1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 735)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 736) /* Release all sensors */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 737) /* One more crappy race: I don't think we have any guarantee here
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 738) * that the attribute callback won't race with the sensor beeing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 739) * disposed of, and I'm not 100% certain what best way to deal
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 740) * with that except by adding locks all over... I'll do that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 741) * eventually but heh, who ever rmmod this module anyway ?
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 742) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 743) if (sensor_cpu_power)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 744) wf_put_sensor(sensor_cpu_power);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 745) if (sensor_cpu_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 746) wf_put_sensor(sensor_cpu_temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 747) if (sensor_hd_temp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 748) wf_put_sensor(sensor_hd_temp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 749)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 750) /* Release all controls */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 751) if (fan_cpu_main)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 752) wf_put_control(fan_cpu_main);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 753) if (fan_hd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 754) wf_put_control(fan_hd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 755) if (fan_system)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 756) wf_put_control(fan_system);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 757) if (cpufreq_clamp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 758) wf_put_control(cpufreq_clamp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 759)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 760) /* Destroy control loops state structures */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 761) kfree(wf_smu_sys_fans);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 762) kfree(wf_smu_cpu_fans);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 763)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 764) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 765) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 766)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 767) static struct platform_driver wf_smu_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 768) .probe = wf_smu_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 769) .remove = wf_smu_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 770) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 771) .name = "windfarm",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 772) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 773) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 774)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 775)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 776) static int __init wf_smu_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 777) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 778) int rc = -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 779)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 780) if (of_machine_is_compatible("PowerMac8,1") ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 781) of_machine_is_compatible("PowerMac8,2"))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 782) rc = wf_init_pm();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 783)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 784) if (rc == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 785) #ifdef MODULE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 786) request_module("windfarm_smu_controls");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 787) request_module("windfarm_smu_sensors");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 788) request_module("windfarm_lm75_sensor");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 789) request_module("windfarm_cpufreq_clamp");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 790)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 791) #endif /* MODULE */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 792) platform_driver_register(&wf_smu_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 793) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 794)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 795) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 796) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 797)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 798) static void __exit wf_smu_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 799) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 800)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 801) platform_driver_unregister(&wf_smu_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 802) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 803)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 804)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 805) module_init(wf_smu_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 806) module_exit(wf_smu_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 807)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 808) MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 809) MODULE_DESCRIPTION("Thermal control logic for iMac G5");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 810) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 811) MODULE_ALIAS("platform:windfarm");