Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
aded76b
Documentation
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
clk
clocksource
connector
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fmc
fpga
gpio
gpu
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
ide
idle
iio
infiniband
input
iommu
ipack
irqchip
isdn
leds
lguest
lightnvm
macintosh
ams
Kconfig
Makefile
adb-iop.c
adb.c
adbhid.c
ans-lcd.c
ans-lcd.h
apm_emu.c
mac_hid.c
macio-adb.c
macio_asic.c
macio_sysfs.c
mediabay.c
rack-meter.c
smu.c
therm_adt746x.c
therm_windtunnel.c
via-cuda.c
via-macii.c
via-maciisi.c
via-pmu-backlight.c
via-pmu-event.c
via-pmu-event.h
via-pmu-led.c
via-pmu.c
via-pmu68k.c
windfarm.h
windfarm_ad7417_sensor.c
windfarm_core.c
windfarm_cpufreq_clamp.c
windfarm_fcu_controls.c
windfarm_lm75_sensor.c
windfarm_lm87_sensor.c
windfarm_max6690_sensor.c
windfarm_mpu.h
windfarm_pid.c
windfarm_pid.h
windfarm_pm112.c
windfarm_pm121.c
windfarm_pm72.c
windfarm_pm81.c
windfarm_pm91.c
windfarm_rm31.c
windfarm_smu_controls.c
windfarm_smu_sat.c
windfarm_smu_sensors.c
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
mtd
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
sn
soc
spi
spmi
ssb
staging
target
tc
thermal
thunderbolt
tty
uio
usb
uwb
vfio
vhost
video
virt
virtio
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.cocciconfig
.get_maintainer.ignore
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
REPORTING-BUGS
Breadcrumbs
linux
/
drivers
/
macintosh
/
windfarm_pm112.c
Blame
Blame
Latest commit
History
History
713 lines (620 loc) · 18 KB
Breadcrumbs
linux
/
drivers
/
macintosh
/
windfarm_pm112.c
Top
File metadata and controls
Code
Blame
713 lines (620 loc) · 18 KB
Raw
/* * Windfarm PowerMac thermal control. * Control loops for machines with SMU and PPC970MP processors. * * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. * * Use and redistribute under the terms of the GNU GPL v2. */ #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/reboot.h> #include <asm/prom.h> #include <asm/smu.h> #include "windfarm.h" #include "windfarm_pid.h" #define VERSION "0.2" #define DEBUG #undef LOTSA_DEBUG #ifdef DEBUG #define DBG(args...) printk(args) #else #define DBG(args...) do { } while(0) #endif #ifdef LOTSA_DEBUG #define DBG_LOTS(args...) printk(args) #else #define DBG_LOTS(args...) do { } while(0) #endif /* define this to force CPU overtemp to 60 degree, useful for testing * the overtemp code */ #undef HACKED_OVERTEMP /* We currently only handle 2 chips, 4 cores... */ #define NR_CHIPS 2 #define NR_CORES 4 #define NR_CPU_FANS 3 * NR_CHIPS /* Controls and sensors */ static struct wf_sensor *sens_cpu_temp[NR_CORES]; static struct wf_sensor *sens_cpu_power[NR_CORES]; static struct wf_sensor *hd_temp; static struct wf_sensor *slots_power; static struct wf_sensor *u4_temp; static struct wf_control *cpu_fans[NR_CPU_FANS]; static char *cpu_fan_names[NR_CPU_FANS] = { "cpu-rear-fan-0", "cpu-rear-fan-1", "cpu-front-fan-0", "cpu-front-fan-1", "cpu-pump-0", "cpu-pump-1", }; static struct wf_control *cpufreq_clamp; /* Second pump isn't required (and isn't actually present) */ #define CPU_FANS_REQD (NR_CPU_FANS - 2) #define FIRST_PUMP 4 #define LAST_PUMP 5 /* We keep a temperature history for average calculation of 180s */ #define CPU_TEMP_HIST_SIZE 180 /* Scale factor for fan speed, *100 */ static int cpu_fan_scale[NR_CPU_FANS] = { 100, 100, 97, /* inlet fans run at 97% of exhaust fan */ 97, 100, /* updated later */ 100, /* updated later */ }; static struct wf_control *backside_fan; static struct wf_control *slots_fan; static struct wf_control *drive_bay_fan; /* PID loop state */ static struct wf_cpu_pid_state cpu_pid[NR_CORES]; static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; static int cpu_thist_pt; static s64 cpu_thist_total; static s32 cpu_all_tmax = 100 << 16; static int cpu_last_target; static struct wf_pid_state backside_pid; static int backside_tick; static struct wf_pid_state slots_pid; static int slots_started; static struct wf_pid_state drive_bay_pid; static int drive_bay_tick; static int nr_cores; static int have_all_controls; static int have_all_sensors; static int started; static int failure_state; #define FAILURE_SENSOR 1 #define FAILURE_FAN 2 #define FAILURE_PERM 4 #define FAILURE_LOW_OVERTEMP 8 #define FAILURE_HIGH_OVERTEMP 16 /* Overtemp values */ #define LOW_OVER_AVERAGE 0 #define LOW_OVER_IMMEDIATE (10 << 16) #define LOW_OVER_CLEAR ((-10) << 16) #define HIGH_OVER_IMMEDIATE (14 << 16) #define HIGH_OVER_AVERAGE (10 << 16) #define HIGH_OVER_IMMEDIATE (14 << 16) /* Implementation... */ static int create_cpu_loop(int cpu) { int chip = cpu / 2; int core = cpu & 1; struct smu_sdbp_header *hdr; struct smu_sdbp_cpupiddata *piddata; struct wf_cpu_pid_param pid; struct wf_control *main_fan = cpu_fans[0]; s32 tmax; int fmin; /* Get PID params from the appropriate SAT */ hdr = smu_sat_get_sdb_partition(chip, 0xC8 + core, NULL); if (hdr == NULL) { printk(KERN_WARNING"windfarm: can't get CPU PID fan config\n"); return -EINVAL; } piddata = (struct smu_sdbp_cpupiddata *)&hdr[1]; /* Get FVT params to get Tmax; if not found, assume default */ hdr = smu_sat_get_sdb_partition(chip, 0xC4 + core, NULL); if (hdr) { struct smu_sdbp_fvt *fvt = (struct smu_sdbp_fvt *)&hdr[1]; tmax = fvt->maxtemp << 16; } else tmax = 95 << 16; /* default to 95 degrees C */ /* We keep a global tmax for overtemp calculations */ if (tmax < cpu_all_tmax) cpu_all_tmax = tmax; /* * Darwin has a minimum fan speed of 1000 rpm for the 4-way and * 515 for the 2-way. That appears to be overkill, so for now, * impose a minimum of 750 or 515. */ fmin = (nr_cores > 2) ? 750 : 515; /* Initialize PID loop */ pid.interval = 1; /* seconds */ pid.history_len = piddata->history_len; pid.gd = piddata->gd; pid.gp = piddata->gp; pid.gr = piddata->gr / piddata->history_len; pid.pmaxadj = (piddata->max_power << 16) - (piddata->power_adj << 8); pid.ttarget = tmax - (piddata->target_temp_delta << 16); pid.tmax = tmax; pid.min = main_fan->ops->get_min(main_fan); pid.max = main_fan->ops->get_max(main_fan); if (pid.min < fmin) pid.min = fmin; wf_cpu_pid_init(&cpu_pid[cpu], &pid); return 0; } static void cpu_max_all_fans(void) { int i; /* We max all CPU fans in case of a sensor error. We also do the * cpufreq clamping now, even if it's supposedly done later by the * generic code anyway, we do it earlier here to react faster */ if (cpufreq_clamp) wf_control_set_max(cpufreq_clamp); for (i = 0; i < NR_CPU_FANS; ++i) if (cpu_fans[i]) wf_control_set_max(cpu_fans[i]); } static int cpu_check_overtemp(s32 temp) { int new_state = 0; s32 t_avg, t_old; /* First check for immediate overtemps */ if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { new_state |= FAILURE_LOW_OVERTEMP; if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" " temperature !\n"); } if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { new_state |= FAILURE_HIGH_OVERTEMP; if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Critical overtemp due to" " immediate CPU temperature !\n"); } /* We calculate a history of max temperatures and use that for the * overtemp management */ t_old = cpu_thist[cpu_thist_pt]; cpu_thist[cpu_thist_pt] = temp; cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; cpu_thist_total -= t_old; cpu_thist_total += temp; t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; DBG_LOTS("t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); /* Now check for average overtemps */ if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { new_state |= FAILURE_LOW_OVERTEMP; if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Overtemp due to average CPU" " temperature !\n"); } if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { new_state |= FAILURE_HIGH_OVERTEMP; if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Critical overtemp due to" " average CPU temperature !\n"); } /* Now handle overtemp conditions. We don't currently use the windfarm * overtemp handling core as it's not fully suited to the needs of those * new machine. This will be fixed later. */ if (new_state) { /* High overtemp -> immediate shutdown */ if (new_state & FAILURE_HIGH_OVERTEMP) machine_power_off(); if ((failure_state & new_state) != new_state) cpu_max_all_fans(); failure_state |= new_state; } else if ((failure_state & FAILURE_LOW_OVERTEMP) && (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); failure_state &= ~FAILURE_LOW_OVERTEMP; } return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); } static void cpu_fans_tick(void) { int err, cpu; s32 greatest_delta = 0; s32 temp, power, t_max = 0; int i, t, target = 0; struct wf_sensor *sr; struct wf_control *ct; struct wf_cpu_pid_state *sp; DBG_LOTS(KERN_DEBUG); for (cpu = 0; cpu < nr_cores; ++cpu) { /* Get CPU core temperature */ sr = sens_cpu_temp[cpu]; err = sr->ops->get_value(sr, &temp); if (err) { DBG("\n"); printk(KERN_WARNING "windfarm: CPU %d temperature " "sensor error %d\n", cpu, err); failure_state |= FAILURE_SENSOR; cpu_max_all_fans(); return; } /* Keep track of highest temp */ t_max = max(t_max, temp); /* Get CPU power */ sr = sens_cpu_power[cpu]; err = sr->ops->get_value(sr, &power); if (err) { DBG("\n"); printk(KERN_WARNING "windfarm: CPU %d power " "sensor error %d\n", cpu, err); failure_state |= FAILURE_SENSOR; cpu_max_all_fans(); return; } /* Run PID */ sp = &cpu_pid[cpu]; t = wf_cpu_pid_run(sp, power, temp); if (cpu == 0 || sp->last_delta > greatest_delta) { greatest_delta = sp->last_delta; target = t; } DBG_LOTS("[%d] P=%d.%.3d T=%d.%.3d ", cpu, FIX32TOPRINT(power), FIX32TOPRINT(temp)); } DBG_LOTS("fans = %d, t_max = %d.%03d\n", target, FIX32TOPRINT(t_max)); /* Darwin limits decrease to 20 per iteration */ if (target < (cpu_last_target - 20)) target = cpu_last_target - 20; cpu_last_target = target; for (cpu = 0; cpu < nr_cores; ++cpu) cpu_pid[cpu].target = target; /* Handle possible overtemps */ if (cpu_check_overtemp(t_max)) return; /* Set fans */ for (i = 0; i < NR_CPU_FANS; ++i) { ct = cpu_fans[i]; if (ct == NULL) continue; err = ct->ops->set_value(ct, target * cpu_fan_scale[i] / 100); if (err) { printk(KERN_WARNING "windfarm: fan %s reports " "error %d\n", ct->name, err); failure_state |= FAILURE_FAN; break; } } } /* Backside/U4 fan */ static struct wf_pid_param backside_param = { .interval = 5, .history_len = 2, .gd = 48 << 20, .gp = 5 << 20, .gr = 0, .itarget = 64 << 16, .additive = 1, }; static void backside_fan_tick(void) { s32 temp; int speed; int err; if (!backside_fan || !u4_temp) return; if (!backside_tick) { /* first time; initialize things */ printk(KERN_INFO "windfarm: Backside control loop started.\n"); backside_param.min = backside_fan->ops->get_min(backside_fan); backside_param.max = backside_fan->ops->get_max(backside_fan); wf_pid_init(&backside_pid, &backside_param); backside_tick = 1; } if (--backside_tick > 0) return; backside_tick = backside_pid.param.interval; err = u4_temp->ops->get_value(u4_temp, &temp); if (err) { printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n", err); failure_state |= FAILURE_SENSOR; wf_control_set_max(backside_fan); return; } speed = wf_pid_run(&backside_pid, temp); DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", FIX32TOPRINT(temp), speed); err = backside_fan->ops->set_value(backside_fan, speed); if (err) { printk(KERN_WARNING "windfarm: backside fan error %d\n", err); failure_state |= FAILURE_FAN; } } /* Drive bay fan */ static struct wf_pid_param drive_bay_prm = { .interval = 5, .history_len = 2, .gd = 30 << 20, .gp = 5 << 20, .gr = 0, .itarget = 40 << 16, .additive = 1, }; static void drive_bay_fan_tick(void) { s32 temp; int speed; int err; if (!drive_bay_fan || !hd_temp) return; if (!drive_bay_tick) { /* first time; initialize things */ printk(KERN_INFO "windfarm: Drive bay control loop started.\n"); drive_bay_prm.min = drive_bay_fan->ops->get_min(drive_bay_fan); drive_bay_prm.max = drive_bay_fan->ops->get_max(drive_bay_fan); wf_pid_init(&drive_bay_pid, &drive_bay_prm); drive_bay_tick = 1; } if (--drive_bay_tick > 0) return; drive_bay_tick = drive_bay_pid.param.interval; err = hd_temp->ops->get_value(hd_temp, &temp); if (err) { printk(KERN_WARNING "windfarm: drive bay temp sensor " "error %d\n", err); failure_state |= FAILURE_SENSOR; wf_control_set_max(drive_bay_fan); return; } speed = wf_pid_run(&drive_bay_pid, temp); DBG_LOTS("drive_bay PID temp=%d.%.3d speed=%d\n", FIX32TOPRINT(temp), speed); err = drive_bay_fan->ops->set_value(drive_bay_fan, speed); if (err) { printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err); failure_state |= FAILURE_FAN; } } /* PCI slots area fan */ /* This makes the fan speed proportional to the power consumed */ static struct wf_pid_param slots_param = { .interval = 1, .history_len = 2, .gd = 0, .gp = 0, .gr = 0x1277952, .itarget = 0, .min = 1560, .max = 3510, }; static void slots_fan_tick(void) { s32 power; int speed; int err; if (!slots_fan || !slots_power) return; if (!slots_started) { /* first time; initialize things */ printk(KERN_INFO "windfarm: Slots control loop started.\n"); wf_pid_init(&slots_pid, &slots_param); slots_started = 1; } err = slots_power->ops->get_value(slots_power, &power); if (err) { printk(KERN_WARNING "windfarm: slots power sensor error %d\n", err); failure_state |= FAILURE_SENSOR; wf_control_set_max(slots_fan); return; } speed = wf_pid_run(&slots_pid, power); DBG_LOTS("slots PID power=%d.%.3d speed=%d\n", FIX32TOPRINT(power), speed); err = slots_fan->ops->set_value(slots_fan, speed); if (err) { printk(KERN_WARNING "windfarm: slots fan error %d\n", err); failure_state |= FAILURE_FAN; } } static void set_fail_state(void) { int i; if (cpufreq_clamp) wf_control_set_max(cpufreq_clamp); for (i = 0; i < NR_CPU_FANS; ++i) if (cpu_fans[i]) wf_control_set_max(cpu_fans[i]); if (backside_fan) wf_control_set_max(backside_fan); if (slots_fan) wf_control_set_max(slots_fan); if (drive_bay_fan) wf_control_set_max(drive_bay_fan); } static void pm112_tick(void) { int i, last_failure; if (!started) { started = 1; printk(KERN_INFO "windfarm: CPUs control loops started.\n"); for (i = 0; i < nr_cores; ++i) { if (create_cpu_loop(i) < 0) { failure_state = FAILURE_PERM; set_fail_state(); break; } } DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); #ifdef HACKED_OVERTEMP cpu_all_tmax = 60 << 16; #endif } /* Permanent failure, bail out */ if (failure_state & FAILURE_PERM) return; /* Clear all failure bits except low overtemp which will be eventually * cleared by the control loop itself */ last_failure = failure_state; failure_state &= FAILURE_LOW_OVERTEMP; cpu_fans_tick(); backside_fan_tick(); slots_fan_tick(); drive_bay_fan_tick(); DBG_LOTS("last_failure: 0x%x, failure_state: %x\n", last_failure, failure_state); /* Check for failures. Any failure causes cpufreq clamping */ if (failure_state && last_failure == 0 && cpufreq_clamp) wf_control_set_max(cpufreq_clamp); if (failure_state == 0 && last_failure && cpufreq_clamp) wf_control_set_min(cpufreq_clamp); /* That's it for now, we might want to deal with other failures * differently in the future though */ } static void pm112_new_control(struct wf_control *ct) { int i, max_exhaust; if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) { if (wf_get_control(ct) == 0) cpufreq_clamp = ct; } for (i = 0; i < NR_CPU_FANS; ++i) { if (!strcmp(ct->name, cpu_fan_names[i])) { if (cpu_fans[i] == NULL && wf_get_control(ct) == 0) cpu_fans[i] = ct; break; } } if (i >= NR_CPU_FANS) { /* not a CPU fan, try the others */ if (!strcmp(ct->name, "backside-fan")) { if (backside_fan == NULL && wf_get_control(ct) == 0) backside_fan = ct; } else if (!strcmp(ct->name, "slots-fan")) { if (slots_fan == NULL && wf_get_control(ct) == 0) slots_fan = ct; } else if (!strcmp(ct->name, "drive-bay-fan")) { if (drive_bay_fan == NULL && wf_get_control(ct) == 0) drive_bay_fan = ct; } return; } for (i = 0; i < CPU_FANS_REQD; ++i) if (cpu_fans[i] == NULL) return; /* work out pump scaling factors */ max_exhaust = cpu_fans[0]->ops->get_max(cpu_fans[0]); for (i = FIRST_PUMP; i <= LAST_PUMP; ++i) if ((ct = cpu_fans[i]) != NULL) cpu_fan_scale[i] = ct->ops->get_max(ct) * 100 / max_exhaust; have_all_controls = 1; } static void pm112_new_sensor(struct wf_sensor *sr) { unsigned int i; if (!strncmp(sr->name, "cpu-temp-", 9)) { i = sr->name[9] - '0'; if (sr->name[10] == 0 && i < NR_CORES && sens_cpu_temp[i] == NULL && wf_get_sensor(sr) == 0) sens_cpu_temp[i] = sr; } else if (!strncmp(sr->name, "cpu-power-", 10)) { i = sr->name[10] - '0'; if (sr->name[11] == 0 && i < NR_CORES && sens_cpu_power[i] == NULL && wf_get_sensor(sr) == 0) sens_cpu_power[i] = sr; } else if (!strcmp(sr->name, "hd-temp")) { if (hd_temp == NULL && wf_get_sensor(sr) == 0) hd_temp = sr; } else if (!strcmp(sr->name, "slots-power")) { if (slots_power == NULL && wf_get_sensor(sr) == 0) slots_power = sr; } else if (!strcmp(sr->name, "backside-temp")) { if (u4_temp == NULL && wf_get_sensor(sr) == 0) u4_temp = sr; } else return; /* check if we have all the sensors we need */ for (i = 0; i < nr_cores; ++i) if (sens_cpu_temp[i] == NULL || sens_cpu_power[i] == NULL) return; have_all_sensors = 1; } static int pm112_wf_notify(struct notifier_block *self, unsigned long event, void *data) { switch (event) { case WF_EVENT_NEW_SENSOR: pm112_new_sensor(data); break; case WF_EVENT_NEW_CONTROL: pm112_new_control(data); break; case WF_EVENT_TICK: if (have_all_controls && have_all_sensors) pm112_tick(); } return 0; } static struct notifier_block pm112_events = { .notifier_call = pm112_wf_notify, }; static int wf_pm112_probe(struct platform_device *dev) { wf_register_client(&pm112_events); return 0; } static int wf_pm112_remove(struct platform_device *dev) { wf_unregister_client(&pm112_events); /* should release all sensors and controls */ return 0; } static struct platform_driver wf_pm112_driver = { .probe = wf_pm112_probe, .remove = wf_pm112_remove, .driver = { .name = "windfarm", }, }; static int __init wf_pm112_init(void) { struct device_node *cpu; if (!of_machine_is_compatible("PowerMac11,2")) return -ENODEV; /* Count the number of CPU cores */ nr_cores = 0; for_each_node_by_type(cpu, "cpu") ++nr_cores; printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n"); #ifdef MODULE request_module("windfarm_smu_controls"); request_module("windfarm_smu_sensors"); request_module("windfarm_smu_sat"); request_module("windfarm_lm75_sensor"); request_module("windfarm_max6690_sensor"); request_module("windfarm_cpufreq_clamp"); #endif /* MODULE */ platform_driver_register(&wf_pm112_driver); return 0; } static void __exit wf_pm112_exit(void) { platform_driver_unregister(&wf_pm112_driver); } module_init(wf_pm112_init); module_exit(wf_pm112_exit); MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); MODULE_DESCRIPTION("Thermal control for PowerMac11,2"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:windfarm");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
You can’t perform that action at this time.