162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Windfarm PowerMac thermal control. iMac G5 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp. 662306a36Sopenharmony_ci * <benh@kernel.crashing.org> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * The algorithm used is the PID control algorithm, used the same 962306a36Sopenharmony_ci * way the published Darwin code does, using the same values that 1062306a36Sopenharmony_ci * are present in the Darwin 8.2 snapshot property lists (note however 1162306a36Sopenharmony_ci * that none of the code has been re-used, it's a complete re-implementation 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * The various control loops found in Darwin config file are: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * PowerMac8,1 and PowerMac8,2 1662306a36Sopenharmony_ci * =========================== 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * System Fans control loop. Different based on models. In addition to the 1962306a36Sopenharmony_ci * usual PID algorithm, the control loop gets 2 additional pairs of linear 2062306a36Sopenharmony_ci * scaling factors (scale/offsets) expressed as 4.12 fixed point values 2162306a36Sopenharmony_ci * signed offset, unsigned scale) 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * The targets are modified such as: 2462306a36Sopenharmony_ci * - the linked control (second control) gets the target value as-is 2562306a36Sopenharmony_ci * (typically the drive fan) 2662306a36Sopenharmony_ci * - the main control (first control) gets the target value scaled with 2762306a36Sopenharmony_ci * the first pair of factors, and is then modified as below 2862306a36Sopenharmony_ci * - the value of the target of the CPU Fan control loop is retrieved, 2962306a36Sopenharmony_ci * scaled with the second pair of factors, and the max of that and 3062306a36Sopenharmony_ci * the scaled target is applied to the main control. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * # model_id: 2 3362306a36Sopenharmony_ci * controls : system-fan, drive-bay-fan 3462306a36Sopenharmony_ci * sensors : hd-temp 3562306a36Sopenharmony_ci * PID params : G_d = 0x15400000 3662306a36Sopenharmony_ci * G_p = 0x00200000 3762306a36Sopenharmony_ci * G_r = 0x000002fd 3862306a36Sopenharmony_ci * History = 2 entries 3962306a36Sopenharmony_ci * Input target = 0x3a0000 4062306a36Sopenharmony_ci * Interval = 5s 4162306a36Sopenharmony_ci * linear-factors : offset = 0xff38 scale = 0x0ccd 4262306a36Sopenharmony_ci * offset = 0x0208 scale = 0x07ae 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * # model_id: 3 4562306a36Sopenharmony_ci * controls : system-fan, drive-bay-fan 4662306a36Sopenharmony_ci * sensors : hd-temp 4762306a36Sopenharmony_ci * PID params : G_d = 0x08e00000 4862306a36Sopenharmony_ci * G_p = 0x00566666 4962306a36Sopenharmony_ci * G_r = 0x0000072b 5062306a36Sopenharmony_ci * History = 2 entries 5162306a36Sopenharmony_ci * Input target = 0x350000 5262306a36Sopenharmony_ci * Interval = 5s 5362306a36Sopenharmony_ci * linear-factors : offset = 0xff38 scale = 0x0ccd 5462306a36Sopenharmony_ci * offset = 0x0000 scale = 0x0000 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * # model_id: 5 5762306a36Sopenharmony_ci * controls : system-fan 5862306a36Sopenharmony_ci * sensors : hd-temp 5962306a36Sopenharmony_ci * PID params : G_d = 0x15400000 6062306a36Sopenharmony_ci * G_p = 0x00233333 6162306a36Sopenharmony_ci * G_r = 0x000002fd 6262306a36Sopenharmony_ci * History = 2 entries 6362306a36Sopenharmony_ci * Input target = 0x3a0000 6462306a36Sopenharmony_ci * Interval = 5s 6562306a36Sopenharmony_ci * linear-factors : offset = 0x0000 scale = 0x1000 6662306a36Sopenharmony_ci * offset = 0x0091 scale = 0x0bae 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * CPU Fan control loop. The loop is identical for all models. it 6962306a36Sopenharmony_ci * has an additional pair of scaling factor. This is used to scale the 7062306a36Sopenharmony_ci * systems fan control loop target result (the one before it gets scaled 7162306a36Sopenharmony_ci * by the System Fans control loop itself). Then, the max value of the 7262306a36Sopenharmony_ci * calculated target value and system fan value is sent to the fans 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * controls : cpu-fan 7562306a36Sopenharmony_ci * sensors : cpu-temp cpu-power 7662306a36Sopenharmony_ci * PID params : From SMU sdb partition 7762306a36Sopenharmony_ci * linear-factors : offset = 0xfb50 scale = 0x1000 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * CPU Slew control loop. Not implemented. The cpufreq driver in linux is 8062306a36Sopenharmony_ci * completely separate for now, though we could find a way to link it, either 8162306a36Sopenharmony_ci * as a client reacting to overtemp notifications, or directling monitoring 8262306a36Sopenharmony_ci * the CPU temperature 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * WARNING ! The CPU control loop requires the CPU tmax for the current 8562306a36Sopenharmony_ci * operating point. However, we currently are completely separated from 8662306a36Sopenharmony_ci * the cpufreq driver and thus do not know what the current operating 8762306a36Sopenharmony_ci * point is. Fortunately, we also do not have any hardware supporting anything 8862306a36Sopenharmony_ci * but operating point 0 at the moment, thus we just peek that value directly 8962306a36Sopenharmony_ci * from the SDB partition. If we ever end up with actually slewing the system 9062306a36Sopenharmony_ci * clock and thus changing operating points, we'll have to find a way to 9162306a36Sopenharmony_ci * communicate with the CPU freq driver; 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#include <linux/types.h> 9562306a36Sopenharmony_ci#include <linux/errno.h> 9662306a36Sopenharmony_ci#include <linux/kernel.h> 9762306a36Sopenharmony_ci#include <linux/delay.h> 9862306a36Sopenharmony_ci#include <linux/slab.h> 9962306a36Sopenharmony_ci#include <linux/init.h> 10062306a36Sopenharmony_ci#include <linux/spinlock.h> 10162306a36Sopenharmony_ci#include <linux/wait.h> 10262306a36Sopenharmony_ci#include <linux/kmod.h> 10362306a36Sopenharmony_ci#include <linux/device.h> 10462306a36Sopenharmony_ci#include <linux/platform_device.h> 10562306a36Sopenharmony_ci#include <linux/of.h> 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#include <asm/machdep.h> 10862306a36Sopenharmony_ci#include <asm/io.h> 10962306a36Sopenharmony_ci#include <asm/sections.h> 11062306a36Sopenharmony_ci#include <asm/smu.h> 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci#include "windfarm.h" 11362306a36Sopenharmony_ci#include "windfarm_pid.h" 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define VERSION "0.4" 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#undef DEBUG 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#ifdef DEBUG 12062306a36Sopenharmony_ci#define DBG(args...) printk(args) 12162306a36Sopenharmony_ci#else 12262306a36Sopenharmony_ci#define DBG(args...) do { } while(0) 12362306a36Sopenharmony_ci#endif 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* define this to force CPU overtemp to 74 degree, useful for testing 12662306a36Sopenharmony_ci * the overtemp code 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci#undef HACKED_OVERTEMP 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int wf_smu_mach_model; /* machine model id */ 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Controls & sensors */ 13362306a36Sopenharmony_cistatic struct wf_sensor *sensor_cpu_power; 13462306a36Sopenharmony_cistatic struct wf_sensor *sensor_cpu_temp; 13562306a36Sopenharmony_cistatic struct wf_sensor *sensor_hd_temp; 13662306a36Sopenharmony_cistatic struct wf_control *fan_cpu_main; 13762306a36Sopenharmony_cistatic struct wf_control *fan_hd; 13862306a36Sopenharmony_cistatic struct wf_control *fan_system; 13962306a36Sopenharmony_cistatic struct wf_control *cpufreq_clamp; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Set to kick the control loop into life */ 14262306a36Sopenharmony_cistatic int wf_smu_all_controls_ok, wf_smu_all_sensors_ok; 14362306a36Sopenharmony_cistatic bool wf_smu_started; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* Failure handling.. could be nicer */ 14662306a36Sopenharmony_ci#define FAILURE_FAN 0x01 14762306a36Sopenharmony_ci#define FAILURE_SENSOR 0x02 14862306a36Sopenharmony_ci#define FAILURE_OVERTEMP 0x04 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic unsigned int wf_smu_failure_state; 15162306a36Sopenharmony_cistatic int wf_smu_readjust, wf_smu_skipping; 15262306a36Sopenharmony_cistatic bool wf_smu_overtemp; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* 15562306a36Sopenharmony_ci * ****** System Fans Control Loop ****** 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Parameters for the System Fans control loop. Parameters 16062306a36Sopenharmony_ci * not in this table such as interval, history size, ... 16162306a36Sopenharmony_ci * are common to all versions and thus hard coded for now. 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_cistruct wf_smu_sys_fans_param { 16462306a36Sopenharmony_ci int model_id; 16562306a36Sopenharmony_ci s32 itarget; 16662306a36Sopenharmony_ci s32 gd, gp, gr; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci s16 offset0; 16962306a36Sopenharmony_ci u16 scale0; 17062306a36Sopenharmony_ci s16 offset1; 17162306a36Sopenharmony_ci u16 scale1; 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define WF_SMU_SYS_FANS_INTERVAL 5 17562306a36Sopenharmony_ci#define WF_SMU_SYS_FANS_HISTORY_SIZE 2 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* State data used by the system fans control loop 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistruct wf_smu_sys_fans_state { 18062306a36Sopenharmony_ci int ticks; 18162306a36Sopenharmony_ci s32 sys_setpoint; 18262306a36Sopenharmony_ci s32 hd_setpoint; 18362306a36Sopenharmony_ci s16 offset0; 18462306a36Sopenharmony_ci u16 scale0; 18562306a36Sopenharmony_ci s16 offset1; 18662306a36Sopenharmony_ci u16 scale1; 18762306a36Sopenharmony_ci struct wf_pid_state pid; 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * Configs for SMU System Fan control loop 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = { 19462306a36Sopenharmony_ci /* Model ID 2 */ 19562306a36Sopenharmony_ci { 19662306a36Sopenharmony_ci .model_id = 2, 19762306a36Sopenharmony_ci .itarget = 0x3a0000, 19862306a36Sopenharmony_ci .gd = 0x15400000, 19962306a36Sopenharmony_ci .gp = 0x00200000, 20062306a36Sopenharmony_ci .gr = 0x000002fd, 20162306a36Sopenharmony_ci .offset0 = 0xff38, 20262306a36Sopenharmony_ci .scale0 = 0x0ccd, 20362306a36Sopenharmony_ci .offset1 = 0x0208, 20462306a36Sopenharmony_ci .scale1 = 0x07ae, 20562306a36Sopenharmony_ci }, 20662306a36Sopenharmony_ci /* Model ID 3 */ 20762306a36Sopenharmony_ci { 20862306a36Sopenharmony_ci .model_id = 3, 20962306a36Sopenharmony_ci .itarget = 0x350000, 21062306a36Sopenharmony_ci .gd = 0x08e00000, 21162306a36Sopenharmony_ci .gp = 0x00566666, 21262306a36Sopenharmony_ci .gr = 0x0000072b, 21362306a36Sopenharmony_ci .offset0 = 0xff38, 21462306a36Sopenharmony_ci .scale0 = 0x0ccd, 21562306a36Sopenharmony_ci .offset1 = 0x0000, 21662306a36Sopenharmony_ci .scale1 = 0x0000, 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci /* Model ID 5 */ 21962306a36Sopenharmony_ci { 22062306a36Sopenharmony_ci .model_id = 5, 22162306a36Sopenharmony_ci .itarget = 0x3a0000, 22262306a36Sopenharmony_ci .gd = 0x15400000, 22362306a36Sopenharmony_ci .gp = 0x00233333, 22462306a36Sopenharmony_ci .gr = 0x000002fd, 22562306a36Sopenharmony_ci .offset0 = 0x0000, 22662306a36Sopenharmony_ci .scale0 = 0x1000, 22762306a36Sopenharmony_ci .offset1 = 0x0091, 22862306a36Sopenharmony_ci .scale1 = 0x0bae, 22962306a36Sopenharmony_ci }, 23062306a36Sopenharmony_ci}; 23162306a36Sopenharmony_ci#define WF_SMU_SYS_FANS_NUM_CONFIGS ARRAY_SIZE(wf_smu_sys_all_params) 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic struct wf_smu_sys_fans_state *wf_smu_sys_fans; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* 23662306a36Sopenharmony_ci * ****** CPU Fans Control Loop ****** 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#define WF_SMU_CPU_FANS_INTERVAL 1 24262306a36Sopenharmony_ci#define WF_SMU_CPU_FANS_MAX_HISTORY 16 24362306a36Sopenharmony_ci#define WF_SMU_CPU_FANS_SIBLING_SCALE 0x00001000 24462306a36Sopenharmony_ci#define WF_SMU_CPU_FANS_SIBLING_OFFSET 0xfffffb50 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* State data used by the cpu fans control loop 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_cistruct wf_smu_cpu_fans_state { 24962306a36Sopenharmony_ci int ticks; 25062306a36Sopenharmony_ci s32 cpu_setpoint; 25162306a36Sopenharmony_ci s32 scale; 25262306a36Sopenharmony_ci s32 offset; 25362306a36Sopenharmony_ci struct wf_cpu_pid_state pid; 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic struct wf_smu_cpu_fans_state *wf_smu_cpu_fans; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * ***** Implementation ***** 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void wf_smu_create_sys_fans(void) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct wf_smu_sys_fans_param *param = NULL; 26862306a36Sopenharmony_ci struct wf_pid_param pid_param; 26962306a36Sopenharmony_ci int i; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* First, locate the params for this model */ 27262306a36Sopenharmony_ci for (i = 0; i < WF_SMU_SYS_FANS_NUM_CONFIGS; i++) 27362306a36Sopenharmony_ci if (wf_smu_sys_all_params[i].model_id == wf_smu_mach_model) { 27462306a36Sopenharmony_ci param = &wf_smu_sys_all_params[i]; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* No params found, put fans to max */ 27962306a36Sopenharmony_ci if (param == NULL) { 28062306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: System fan config not found " 28162306a36Sopenharmony_ci "for this machine model, max fan speed\n"); 28262306a36Sopenharmony_ci goto fail; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Alloc & initialize state */ 28662306a36Sopenharmony_ci wf_smu_sys_fans = kmalloc(sizeof(struct wf_smu_sys_fans_state), 28762306a36Sopenharmony_ci GFP_KERNEL); 28862306a36Sopenharmony_ci if (wf_smu_sys_fans == NULL) { 28962306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: Memory allocation error" 29062306a36Sopenharmony_ci " max fan speed\n"); 29162306a36Sopenharmony_ci goto fail; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci wf_smu_sys_fans->ticks = 1; 29462306a36Sopenharmony_ci wf_smu_sys_fans->scale0 = param->scale0; 29562306a36Sopenharmony_ci wf_smu_sys_fans->offset0 = param->offset0; 29662306a36Sopenharmony_ci wf_smu_sys_fans->scale1 = param->scale1; 29762306a36Sopenharmony_ci wf_smu_sys_fans->offset1 = param->offset1; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Fill PID params */ 30062306a36Sopenharmony_ci pid_param.gd = param->gd; 30162306a36Sopenharmony_ci pid_param.gp = param->gp; 30262306a36Sopenharmony_ci pid_param.gr = param->gr; 30362306a36Sopenharmony_ci pid_param.interval = WF_SMU_SYS_FANS_INTERVAL; 30462306a36Sopenharmony_ci pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE; 30562306a36Sopenharmony_ci pid_param.itarget = param->itarget; 30662306a36Sopenharmony_ci pid_param.min = wf_control_get_min(fan_system); 30762306a36Sopenharmony_ci pid_param.max = wf_control_get_max(fan_system); 30862306a36Sopenharmony_ci if (fan_hd) { 30962306a36Sopenharmony_ci pid_param.min = 31062306a36Sopenharmony_ci max(pid_param.min, wf_control_get_min(fan_hd)); 31162306a36Sopenharmony_ci pid_param.max = 31262306a36Sopenharmony_ci min(pid_param.max, wf_control_get_max(fan_hd)); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci wf_pid_init(&wf_smu_sys_fans->pid, &pid_param); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci DBG("wf: System Fan control initialized.\n"); 31762306a36Sopenharmony_ci DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n", 31862306a36Sopenharmony_ci FIX32TOPRINT(pid_param.itarget), pid_param.min, pid_param.max); 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci fail: 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (fan_system) 32462306a36Sopenharmony_ci wf_control_set_max(fan_system); 32562306a36Sopenharmony_ci if (fan_hd) 32662306a36Sopenharmony_ci wf_control_set_max(fan_hd); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci s32 new_setpoint, temp, scaled, cputarget; 33262306a36Sopenharmony_ci int rc; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (--st->ticks != 0) { 33562306a36Sopenharmony_ci if (wf_smu_readjust) 33662306a36Sopenharmony_ci goto readjust; 33762306a36Sopenharmony_ci return; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci st->ticks = WF_SMU_SYS_FANS_INTERVAL; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci rc = wf_sensor_get(sensor_hd_temp, &temp); 34262306a36Sopenharmony_ci if (rc) { 34362306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", 34462306a36Sopenharmony_ci rc); 34562306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_SENSOR; 34662306a36Sopenharmony_ci return; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci DBG("wf_smu: System Fans tick ! HD temp: %d.%03d\n", 35062306a36Sopenharmony_ci FIX32TOPRINT(temp)); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (temp > (st->pid.param.itarget + 0x50000)) 35362306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_OVERTEMP; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci new_setpoint = wf_pid_run(&st->pid, temp); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci scaled = ((((s64)new_setpoint) * (s64)st->scale0) >> 12) + st->offset0; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci DBG("wf_smu: scaled setpoint: %d RPM\n", (int)scaled); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci cputarget = wf_smu_cpu_fans ? wf_smu_cpu_fans->pid.target : 0; 36462306a36Sopenharmony_ci cputarget = ((((s64)cputarget) * (s64)st->scale1) >> 12) + st->offset1; 36562306a36Sopenharmony_ci scaled = max(scaled, cputarget); 36662306a36Sopenharmony_ci scaled = max(scaled, st->pid.param.min); 36762306a36Sopenharmony_ci scaled = min(scaled, st->pid.param.max); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)scaled); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (st->sys_setpoint == scaled && new_setpoint == st->hd_setpoint) 37262306a36Sopenharmony_ci return; 37362306a36Sopenharmony_ci st->sys_setpoint = scaled; 37462306a36Sopenharmony_ci st->hd_setpoint = new_setpoint; 37562306a36Sopenharmony_ci readjust: 37662306a36Sopenharmony_ci if (fan_system && wf_smu_failure_state == 0) { 37762306a36Sopenharmony_ci rc = wf_control_set(fan_system, st->sys_setpoint); 37862306a36Sopenharmony_ci if (rc) { 37962306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: Sys fan error %d\n", 38062306a36Sopenharmony_ci rc); 38162306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_FAN; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci if (fan_hd && wf_smu_failure_state == 0) { 38562306a36Sopenharmony_ci rc = wf_control_set(fan_hd, st->hd_setpoint); 38662306a36Sopenharmony_ci if (rc) { 38762306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: HD fan error %d\n", 38862306a36Sopenharmony_ci rc); 38962306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_FAN; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void wf_smu_create_cpu_fans(void) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct wf_cpu_pid_param pid_param; 39762306a36Sopenharmony_ci const struct smu_sdbp_header *hdr; 39862306a36Sopenharmony_ci struct smu_sdbp_cpupiddata *piddata; 39962306a36Sopenharmony_ci struct smu_sdbp_fvt *fvt; 40062306a36Sopenharmony_ci s32 tmax, tdelta, maxpow, powadj; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* First, locate the PID params in SMU SBD */ 40362306a36Sopenharmony_ci hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL); 40462306a36Sopenharmony_ci if (!hdr) { 40562306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: CPU PID fan config not found " 40662306a36Sopenharmony_ci "max fan speed\n"); 40762306a36Sopenharmony_ci goto fail; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci piddata = (struct smu_sdbp_cpupiddata *)&hdr[1]; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Get the FVT params for operating point 0 (the only supported one 41262306a36Sopenharmony_ci * for now) in order to get tmax 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL); 41562306a36Sopenharmony_ci if (hdr) { 41662306a36Sopenharmony_ci fvt = (struct smu_sdbp_fvt *)&hdr[1]; 41762306a36Sopenharmony_ci tmax = ((s32)fvt->maxtemp) << 16; 41862306a36Sopenharmony_ci } else 41962306a36Sopenharmony_ci tmax = 0x5e0000; /* 94 degree default */ 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Alloc & initialize state */ 42262306a36Sopenharmony_ci wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state), 42362306a36Sopenharmony_ci GFP_KERNEL); 42462306a36Sopenharmony_ci if (wf_smu_cpu_fans == NULL) 42562306a36Sopenharmony_ci goto fail; 42662306a36Sopenharmony_ci wf_smu_cpu_fans->ticks = 1; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci wf_smu_cpu_fans->scale = WF_SMU_CPU_FANS_SIBLING_SCALE; 42962306a36Sopenharmony_ci wf_smu_cpu_fans->offset = WF_SMU_CPU_FANS_SIBLING_OFFSET; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Fill PID params */ 43262306a36Sopenharmony_ci pid_param.interval = WF_SMU_CPU_FANS_INTERVAL; 43362306a36Sopenharmony_ci pid_param.history_len = piddata->history_len; 43462306a36Sopenharmony_ci if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) { 43562306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: History size overflow on " 43662306a36Sopenharmony_ci "CPU control loop (%d)\n", piddata->history_len); 43762306a36Sopenharmony_ci pid_param.history_len = WF_CPU_PID_MAX_HISTORY; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci pid_param.gd = piddata->gd; 44062306a36Sopenharmony_ci pid_param.gp = piddata->gp; 44162306a36Sopenharmony_ci pid_param.gr = piddata->gr / pid_param.history_len; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci tdelta = ((s32)piddata->target_temp_delta) << 16; 44462306a36Sopenharmony_ci maxpow = ((s32)piddata->max_power) << 16; 44562306a36Sopenharmony_ci powadj = ((s32)piddata->power_adj) << 16; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci pid_param.tmax = tmax; 44862306a36Sopenharmony_ci pid_param.ttarget = tmax - tdelta; 44962306a36Sopenharmony_ci pid_param.pmaxadj = maxpow - powadj; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci pid_param.min = wf_control_get_min(fan_cpu_main); 45262306a36Sopenharmony_ci pid_param.max = wf_control_get_max(fan_cpu_main); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci DBG("wf: CPU Fan control initialized.\n"); 45762306a36Sopenharmony_ci DBG(" ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n", 45862306a36Sopenharmony_ci FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax), 45962306a36Sopenharmony_ci pid_param.min, pid_param.max); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci fail: 46462306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: CPU fan config not found\n" 46562306a36Sopenharmony_ci "for this machine model, max fan speed\n"); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (cpufreq_clamp) 46862306a36Sopenharmony_ci wf_control_set_max(cpufreq_clamp); 46962306a36Sopenharmony_ci if (fan_cpu_main) 47062306a36Sopenharmony_ci wf_control_set_max(fan_cpu_main); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci s32 new_setpoint, temp, power, systarget; 47662306a36Sopenharmony_ci int rc; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (--st->ticks != 0) { 47962306a36Sopenharmony_ci if (wf_smu_readjust) 48062306a36Sopenharmony_ci goto readjust; 48162306a36Sopenharmony_ci return; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci st->ticks = WF_SMU_CPU_FANS_INTERVAL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci rc = wf_sensor_get(sensor_cpu_temp, &temp); 48662306a36Sopenharmony_ci if (rc) { 48762306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", 48862306a36Sopenharmony_ci rc); 48962306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_SENSOR; 49062306a36Sopenharmony_ci return; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci rc = wf_sensor_get(sensor_cpu_power, &power); 49462306a36Sopenharmony_ci if (rc) { 49562306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", 49662306a36Sopenharmony_ci rc); 49762306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_SENSOR; 49862306a36Sopenharmony_ci return; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n", 50262306a36Sopenharmony_ci FIX32TOPRINT(temp), FIX32TOPRINT(power)); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci#ifdef HACKED_OVERTEMP 50562306a36Sopenharmony_ci if (temp > 0x4a0000) 50662306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_OVERTEMP; 50762306a36Sopenharmony_ci#else 50862306a36Sopenharmony_ci if (temp > st->pid.param.tmax) 50962306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_OVERTEMP; 51062306a36Sopenharmony_ci#endif 51162306a36Sopenharmony_ci new_setpoint = wf_cpu_pid_run(&st->pid, power, temp); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci systarget = wf_smu_sys_fans ? wf_smu_sys_fans->pid.target : 0; 51662306a36Sopenharmony_ci systarget = ((((s64)systarget) * (s64)st->scale) >> 12) 51762306a36Sopenharmony_ci + st->offset; 51862306a36Sopenharmony_ci new_setpoint = max(new_setpoint, systarget); 51962306a36Sopenharmony_ci new_setpoint = max(new_setpoint, st->pid.param.min); 52062306a36Sopenharmony_ci new_setpoint = min(new_setpoint, st->pid.param.max); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)new_setpoint); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (st->cpu_setpoint == new_setpoint) 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci st->cpu_setpoint = new_setpoint; 52762306a36Sopenharmony_ci readjust: 52862306a36Sopenharmony_ci if (fan_cpu_main && wf_smu_failure_state == 0) { 52962306a36Sopenharmony_ci rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); 53062306a36Sopenharmony_ci if (rc) { 53162306a36Sopenharmony_ci printk(KERN_WARNING "windfarm: CPU main fan" 53262306a36Sopenharmony_ci " error %d\n", rc); 53362306a36Sopenharmony_ci wf_smu_failure_state |= FAILURE_FAN; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/* 53962306a36Sopenharmony_ci * ****** Setup / Init / Misc ... ****** 54062306a36Sopenharmony_ci * 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic void wf_smu_tick(void) 54462306a36Sopenharmony_ci{ 54562306a36Sopenharmony_ci unsigned int last_failure = wf_smu_failure_state; 54662306a36Sopenharmony_ci unsigned int new_failure; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (!wf_smu_started) { 54962306a36Sopenharmony_ci DBG("wf: creating control loops !\n"); 55062306a36Sopenharmony_ci wf_smu_create_sys_fans(); 55162306a36Sopenharmony_ci wf_smu_create_cpu_fans(); 55262306a36Sopenharmony_ci wf_smu_started = true; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* Skipping ticks */ 55662306a36Sopenharmony_ci if (wf_smu_skipping && --wf_smu_skipping) 55762306a36Sopenharmony_ci return; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci wf_smu_failure_state = 0; 56062306a36Sopenharmony_ci if (wf_smu_sys_fans) 56162306a36Sopenharmony_ci wf_smu_sys_fans_tick(wf_smu_sys_fans); 56262306a36Sopenharmony_ci if (wf_smu_cpu_fans) 56362306a36Sopenharmony_ci wf_smu_cpu_fans_tick(wf_smu_cpu_fans); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci wf_smu_readjust = 0; 56662306a36Sopenharmony_ci new_failure = wf_smu_failure_state & ~last_failure; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* If entering failure mode, clamp cpufreq and ramp all 56962306a36Sopenharmony_ci * fans to full speed. 57062306a36Sopenharmony_ci */ 57162306a36Sopenharmony_ci if (wf_smu_failure_state && !last_failure) { 57262306a36Sopenharmony_ci if (cpufreq_clamp) 57362306a36Sopenharmony_ci wf_control_set_max(cpufreq_clamp); 57462306a36Sopenharmony_ci if (fan_system) 57562306a36Sopenharmony_ci wf_control_set_max(fan_system); 57662306a36Sopenharmony_ci if (fan_cpu_main) 57762306a36Sopenharmony_ci wf_control_set_max(fan_cpu_main); 57862306a36Sopenharmony_ci if (fan_hd) 57962306a36Sopenharmony_ci wf_control_set_max(fan_hd); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci /* If leaving failure mode, unclamp cpufreq and readjust 58362306a36Sopenharmony_ci * all fans on next iteration 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci if (!wf_smu_failure_state && last_failure) { 58662306a36Sopenharmony_ci if (cpufreq_clamp) 58762306a36Sopenharmony_ci wf_control_set_min(cpufreq_clamp); 58862306a36Sopenharmony_ci wf_smu_readjust = 1; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Overtemp condition detected, notify and start skipping a couple 59262306a36Sopenharmony_ci * ticks to let the temperature go down 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci if (new_failure & FAILURE_OVERTEMP) { 59562306a36Sopenharmony_ci wf_set_overtemp(); 59662306a36Sopenharmony_ci wf_smu_skipping = 2; 59762306a36Sopenharmony_ci wf_smu_overtemp = true; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* We only clear the overtemp condition if overtemp is cleared 60162306a36Sopenharmony_ci * _and_ no other failure is present. Since a sensor error will 60262306a36Sopenharmony_ci * clear the overtemp condition (can't measure temperature) at 60362306a36Sopenharmony_ci * the control loop levels, but we don't want to keep it clear 60462306a36Sopenharmony_ci * here in this case 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci if (!wf_smu_failure_state && wf_smu_overtemp) { 60762306a36Sopenharmony_ci wf_clear_overtemp(); 60862306a36Sopenharmony_ci wf_smu_overtemp = false; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic void wf_smu_new_control(struct wf_control *ct) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci if (wf_smu_all_controls_ok) 61562306a36Sopenharmony_ci return; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) { 61862306a36Sopenharmony_ci if (wf_get_control(ct) == 0) 61962306a36Sopenharmony_ci fan_cpu_main = ct; 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (fan_system == NULL && !strcmp(ct->name, "system-fan")) { 62362306a36Sopenharmony_ci if (wf_get_control(ct) == 0) 62462306a36Sopenharmony_ci fan_system = ct; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) { 62862306a36Sopenharmony_ci if (wf_get_control(ct) == 0) 62962306a36Sopenharmony_ci cpufreq_clamp = ct; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Darwin property list says the HD fan is only for model ID 63362306a36Sopenharmony_ci * 0, 1, 2 and 3 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (wf_smu_mach_model > 3) { 63762306a36Sopenharmony_ci if (fan_system && fan_cpu_main && cpufreq_clamp) 63862306a36Sopenharmony_ci wf_smu_all_controls_ok = 1; 63962306a36Sopenharmony_ci return; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) { 64362306a36Sopenharmony_ci if (wf_get_control(ct) == 0) 64462306a36Sopenharmony_ci fan_hd = ct; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp) 64862306a36Sopenharmony_ci wf_smu_all_controls_ok = 1; 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic void wf_smu_new_sensor(struct wf_sensor *sr) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci if (wf_smu_all_sensors_ok) 65462306a36Sopenharmony_ci return; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) { 65762306a36Sopenharmony_ci if (wf_get_sensor(sr) == 0) 65862306a36Sopenharmony_ci sensor_cpu_power = sr; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) { 66262306a36Sopenharmony_ci if (wf_get_sensor(sr) == 0) 66362306a36Sopenharmony_ci sensor_cpu_temp = sr; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) { 66762306a36Sopenharmony_ci if (wf_get_sensor(sr) == 0) 66862306a36Sopenharmony_ci sensor_hd_temp = sr; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp) 67262306a36Sopenharmony_ci wf_smu_all_sensors_ok = 1; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic int wf_smu_notify(struct notifier_block *self, 67762306a36Sopenharmony_ci unsigned long event, void *data) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci switch(event) { 68062306a36Sopenharmony_ci case WF_EVENT_NEW_CONTROL: 68162306a36Sopenharmony_ci DBG("wf: new control %s detected\n", 68262306a36Sopenharmony_ci ((struct wf_control *)data)->name); 68362306a36Sopenharmony_ci wf_smu_new_control(data); 68462306a36Sopenharmony_ci wf_smu_readjust = 1; 68562306a36Sopenharmony_ci break; 68662306a36Sopenharmony_ci case WF_EVENT_NEW_SENSOR: 68762306a36Sopenharmony_ci DBG("wf: new sensor %s detected\n", 68862306a36Sopenharmony_ci ((struct wf_sensor *)data)->name); 68962306a36Sopenharmony_ci wf_smu_new_sensor(data); 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci case WF_EVENT_TICK: 69262306a36Sopenharmony_ci if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok) 69362306a36Sopenharmony_ci wf_smu_tick(); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci return 0; 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic struct notifier_block wf_smu_events = { 70062306a36Sopenharmony_ci .notifier_call = wf_smu_notify, 70162306a36Sopenharmony_ci}; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int wf_init_pm(void) 70462306a36Sopenharmony_ci{ 70562306a36Sopenharmony_ci const struct smu_sdbp_header *hdr; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL); 70862306a36Sopenharmony_ci if (hdr) { 70962306a36Sopenharmony_ci struct smu_sdbp_sensortree *st = 71062306a36Sopenharmony_ci (struct smu_sdbp_sensortree *)&hdr[1]; 71162306a36Sopenharmony_ci wf_smu_mach_model = st->model_id; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci printk(KERN_INFO "windfarm: Initializing for iMacG5 model ID %d\n", 71562306a36Sopenharmony_ci wf_smu_mach_model); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci return 0; 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic int wf_smu_probe(struct platform_device *ddev) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci wf_register_client(&wf_smu_events); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return 0; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic int wf_smu_remove(struct platform_device *ddev) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci wf_unregister_client(&wf_smu_events); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci /* XXX We don't have yet a guarantee that our callback isn't 73262306a36Sopenharmony_ci * in progress when returning from wf_unregister_client, so 73362306a36Sopenharmony_ci * we add an arbitrary delay. I'll have to fix that in the core 73462306a36Sopenharmony_ci */ 73562306a36Sopenharmony_ci msleep(1000); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Release all sensors */ 73862306a36Sopenharmony_ci /* One more crappy race: I don't think we have any guarantee here 73962306a36Sopenharmony_ci * that the attribute callback won't race with the sensor beeing 74062306a36Sopenharmony_ci * disposed of, and I'm not 100% certain what best way to deal 74162306a36Sopenharmony_ci * with that except by adding locks all over... I'll do that 74262306a36Sopenharmony_ci * eventually but heh, who ever rmmod this module anyway ? 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_ci if (sensor_cpu_power) 74562306a36Sopenharmony_ci wf_put_sensor(sensor_cpu_power); 74662306a36Sopenharmony_ci if (sensor_cpu_temp) 74762306a36Sopenharmony_ci wf_put_sensor(sensor_cpu_temp); 74862306a36Sopenharmony_ci if (sensor_hd_temp) 74962306a36Sopenharmony_ci wf_put_sensor(sensor_hd_temp); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Release all controls */ 75262306a36Sopenharmony_ci if (fan_cpu_main) 75362306a36Sopenharmony_ci wf_put_control(fan_cpu_main); 75462306a36Sopenharmony_ci if (fan_hd) 75562306a36Sopenharmony_ci wf_put_control(fan_hd); 75662306a36Sopenharmony_ci if (fan_system) 75762306a36Sopenharmony_ci wf_put_control(fan_system); 75862306a36Sopenharmony_ci if (cpufreq_clamp) 75962306a36Sopenharmony_ci wf_put_control(cpufreq_clamp); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* Destroy control loops state structures */ 76262306a36Sopenharmony_ci kfree(wf_smu_sys_fans); 76362306a36Sopenharmony_ci kfree(wf_smu_cpu_fans); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic struct platform_driver wf_smu_driver = { 76962306a36Sopenharmony_ci .probe = wf_smu_probe, 77062306a36Sopenharmony_ci .remove = wf_smu_remove, 77162306a36Sopenharmony_ci .driver = { 77262306a36Sopenharmony_ci .name = "windfarm", 77362306a36Sopenharmony_ci }, 77462306a36Sopenharmony_ci}; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic int __init wf_smu_init(void) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci int rc = -ENODEV; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (of_machine_is_compatible("PowerMac8,1") || 78262306a36Sopenharmony_ci of_machine_is_compatible("PowerMac8,2")) 78362306a36Sopenharmony_ci rc = wf_init_pm(); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (rc == 0) { 78662306a36Sopenharmony_ci#ifdef MODULE 78762306a36Sopenharmony_ci request_module("windfarm_smu_controls"); 78862306a36Sopenharmony_ci request_module("windfarm_smu_sensors"); 78962306a36Sopenharmony_ci request_module("windfarm_lm75_sensor"); 79062306a36Sopenharmony_ci request_module("windfarm_cpufreq_clamp"); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci#endif /* MODULE */ 79362306a36Sopenharmony_ci platform_driver_register(&wf_smu_driver); 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci return rc; 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic void __exit wf_smu_exit(void) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci platform_driver_unregister(&wf_smu_driver); 80362306a36Sopenharmony_ci} 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cimodule_init(wf_smu_init); 80762306a36Sopenharmony_cimodule_exit(wf_smu_exit); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 81062306a36Sopenharmony_ciMODULE_DESCRIPTION("Thermal control logic for iMac G5"); 81162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 81262306a36Sopenharmony_ciMODULE_ALIAS("platform:windfarm"); 813