0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094 #include <linux/types.h>
0095 #include <linux/errno.h>
0096 #include <linux/kernel.h>
0097 #include <linux/delay.h>
0098 #include <linux/slab.h>
0099 #include <linux/init.h>
0100 #include <linux/spinlock.h>
0101 #include <linux/wait.h>
0102 #include <linux/kmod.h>
0103 #include <linux/device.h>
0104 #include <linux/platform_device.h>
0105 #include <linux/of.h>
0106
0107 #include <asm/machdep.h>
0108 #include <asm/io.h>
0109 #include <asm/sections.h>
0110 #include <asm/smu.h>
0111
0112 #include "windfarm.h"
0113 #include "windfarm_pid.h"
0114
0115 #define VERSION "0.4"
0116
0117 #undef DEBUG
0118
0119 #ifdef DEBUG
0120 #define DBG(args...) printk(args)
0121 #else
0122 #define DBG(args...) do { } while(0)
0123 #endif
0124
0125
0126
0127
0128 #undef HACKED_OVERTEMP
0129
0130 static int wf_smu_mach_model;
0131
0132
0133 static struct wf_sensor *sensor_cpu_power;
0134 static struct wf_sensor *sensor_cpu_temp;
0135 static struct wf_sensor *sensor_hd_temp;
0136 static struct wf_control *fan_cpu_main;
0137 static struct wf_control *fan_hd;
0138 static struct wf_control *fan_system;
0139 static struct wf_control *cpufreq_clamp;
0140
0141
0142 static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok;
0143 static bool wf_smu_started;
0144
0145
0146 #define FAILURE_FAN 0x01
0147 #define FAILURE_SENSOR 0x02
0148 #define FAILURE_OVERTEMP 0x04
0149
0150 static unsigned int wf_smu_failure_state;
0151 static int wf_smu_readjust, wf_smu_skipping;
0152 static bool wf_smu_overtemp;
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163 struct wf_smu_sys_fans_param {
0164 int model_id;
0165 s32 itarget;
0166 s32 gd, gp, gr;
0167
0168 s16 offset0;
0169 u16 scale0;
0170 s16 offset1;
0171 u16 scale1;
0172 };
0173
0174 #define WF_SMU_SYS_FANS_INTERVAL 5
0175 #define WF_SMU_SYS_FANS_HISTORY_SIZE 2
0176
0177
0178
0179 struct wf_smu_sys_fans_state {
0180 int ticks;
0181 s32 sys_setpoint;
0182 s32 hd_setpoint;
0183 s16 offset0;
0184 u16 scale0;
0185 s16 offset1;
0186 u16 scale1;
0187 struct wf_pid_state pid;
0188 };
0189
0190
0191
0192
0193 static struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = {
0194
0195 {
0196 .model_id = 2,
0197 .itarget = 0x3a0000,
0198 .gd = 0x15400000,
0199 .gp = 0x00200000,
0200 .gr = 0x000002fd,
0201 .offset0 = 0xff38,
0202 .scale0 = 0x0ccd,
0203 .offset1 = 0x0208,
0204 .scale1 = 0x07ae,
0205 },
0206
0207 {
0208 .model_id = 3,
0209 .itarget = 0x350000,
0210 .gd = 0x08e00000,
0211 .gp = 0x00566666,
0212 .gr = 0x0000072b,
0213 .offset0 = 0xff38,
0214 .scale0 = 0x0ccd,
0215 .offset1 = 0x0000,
0216 .scale1 = 0x0000,
0217 },
0218
0219 {
0220 .model_id = 5,
0221 .itarget = 0x3a0000,
0222 .gd = 0x15400000,
0223 .gp = 0x00233333,
0224 .gr = 0x000002fd,
0225 .offset0 = 0x0000,
0226 .scale0 = 0x1000,
0227 .offset1 = 0x0091,
0228 .scale1 = 0x0bae,
0229 },
0230 };
0231 #define WF_SMU_SYS_FANS_NUM_CONFIGS ARRAY_SIZE(wf_smu_sys_all_params)
0232
0233 static struct wf_smu_sys_fans_state *wf_smu_sys_fans;
0234
0235
0236
0237
0238
0239
0240
0241 #define WF_SMU_CPU_FANS_INTERVAL 1
0242 #define WF_SMU_CPU_FANS_MAX_HISTORY 16
0243 #define WF_SMU_CPU_FANS_SIBLING_SCALE 0x00001000
0244 #define WF_SMU_CPU_FANS_SIBLING_OFFSET 0xfffffb50
0245
0246
0247
0248 struct wf_smu_cpu_fans_state {
0249 int ticks;
0250 s32 cpu_setpoint;
0251 s32 scale;
0252 s32 offset;
0253 struct wf_cpu_pid_state pid;
0254 };
0255
0256 static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;
0257
0258
0259
0260
0261
0262
0263
0264
0265 static void wf_smu_create_sys_fans(void)
0266 {
0267 struct wf_smu_sys_fans_param *param = NULL;
0268 struct wf_pid_param pid_param;
0269 int i;
0270
0271
0272 for (i = 0; i < WF_SMU_SYS_FANS_NUM_CONFIGS; i++)
0273 if (wf_smu_sys_all_params[i].model_id == wf_smu_mach_model) {
0274 param = &wf_smu_sys_all_params[i];
0275 break;
0276 }
0277
0278
0279 if (param == NULL) {
0280 printk(KERN_WARNING "windfarm: System fan config not found "
0281 "for this machine model, max fan speed\n");
0282 goto fail;
0283 }
0284
0285
0286 wf_smu_sys_fans = kmalloc(sizeof(struct wf_smu_sys_fans_state),
0287 GFP_KERNEL);
0288 if (wf_smu_sys_fans == NULL) {
0289 printk(KERN_WARNING "windfarm: Memory allocation error"
0290 " max fan speed\n");
0291 goto fail;
0292 }
0293 wf_smu_sys_fans->ticks = 1;
0294 wf_smu_sys_fans->scale0 = param->scale0;
0295 wf_smu_sys_fans->offset0 = param->offset0;
0296 wf_smu_sys_fans->scale1 = param->scale1;
0297 wf_smu_sys_fans->offset1 = param->offset1;
0298
0299
0300 pid_param.gd = param->gd;
0301 pid_param.gp = param->gp;
0302 pid_param.gr = param->gr;
0303 pid_param.interval = WF_SMU_SYS_FANS_INTERVAL;
0304 pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE;
0305 pid_param.itarget = param->itarget;
0306 pid_param.min = wf_control_get_min(fan_system);
0307 pid_param.max = wf_control_get_max(fan_system);
0308 if (fan_hd) {
0309 pid_param.min =
0310 max(pid_param.min, wf_control_get_min(fan_hd));
0311 pid_param.max =
0312 min(pid_param.max, wf_control_get_max(fan_hd));
0313 }
0314 wf_pid_init(&wf_smu_sys_fans->pid, &pid_param);
0315
0316 DBG("wf: System Fan control initialized.\n");
0317 DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
0318 FIX32TOPRINT(pid_param.itarget), pid_param.min, pid_param.max);
0319 return;
0320
0321 fail:
0322
0323 if (fan_system)
0324 wf_control_set_max(fan_system);
0325 if (fan_hd)
0326 wf_control_set_max(fan_hd);
0327 }
0328
0329 static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)
0330 {
0331 s32 new_setpoint, temp, scaled, cputarget;
0332 int rc;
0333
0334 if (--st->ticks != 0) {
0335 if (wf_smu_readjust)
0336 goto readjust;
0337 return;
0338 }
0339 st->ticks = WF_SMU_SYS_FANS_INTERVAL;
0340
0341 rc = wf_sensor_get(sensor_hd_temp, &temp);
0342 if (rc) {
0343 printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
0344 rc);
0345 wf_smu_failure_state |= FAILURE_SENSOR;
0346 return;
0347 }
0348
0349 DBG("wf_smu: System Fans tick ! HD temp: %d.%03d\n",
0350 FIX32TOPRINT(temp));
0351
0352 if (temp > (st->pid.param.itarget + 0x50000))
0353 wf_smu_failure_state |= FAILURE_OVERTEMP;
0354
0355 new_setpoint = wf_pid_run(&st->pid, temp);
0356
0357 DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
0358
0359 scaled = ((((s64)new_setpoint) * (s64)st->scale0) >> 12) + st->offset0;
0360
0361 DBG("wf_smu: scaled setpoint: %d RPM\n", (int)scaled);
0362
0363 cputarget = wf_smu_cpu_fans ? wf_smu_cpu_fans->pid.target : 0;
0364 cputarget = ((((s64)cputarget) * (s64)st->scale1) >> 12) + st->offset1;
0365 scaled = max(scaled, cputarget);
0366 scaled = max(scaled, st->pid.param.min);
0367 scaled = min(scaled, st->pid.param.max);
0368
0369 DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)scaled);
0370
0371 if (st->sys_setpoint == scaled && new_setpoint == st->hd_setpoint)
0372 return;
0373 st->sys_setpoint = scaled;
0374 st->hd_setpoint = new_setpoint;
0375 readjust:
0376 if (fan_system && wf_smu_failure_state == 0) {
0377 rc = wf_control_set(fan_system, st->sys_setpoint);
0378 if (rc) {
0379 printk(KERN_WARNING "windfarm: Sys fan error %d\n",
0380 rc);
0381 wf_smu_failure_state |= FAILURE_FAN;
0382 }
0383 }
0384 if (fan_hd && wf_smu_failure_state == 0) {
0385 rc = wf_control_set(fan_hd, st->hd_setpoint);
0386 if (rc) {
0387 printk(KERN_WARNING "windfarm: HD fan error %d\n",
0388 rc);
0389 wf_smu_failure_state |= FAILURE_FAN;
0390 }
0391 }
0392 }
0393
0394 static void wf_smu_create_cpu_fans(void)
0395 {
0396 struct wf_cpu_pid_param pid_param;
0397 const struct smu_sdbp_header *hdr;
0398 struct smu_sdbp_cpupiddata *piddata;
0399 struct smu_sdbp_fvt *fvt;
0400 s32 tmax, tdelta, maxpow, powadj;
0401
0402
0403 hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
0404 if (hdr == 0) {
0405 printk(KERN_WARNING "windfarm: CPU PID fan config not found "
0406 "max fan speed\n");
0407 goto fail;
0408 }
0409 piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
0410
0411
0412
0413
0414 hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
0415 if (hdr) {
0416 fvt = (struct smu_sdbp_fvt *)&hdr[1];
0417 tmax = ((s32)fvt->maxtemp) << 16;
0418 } else
0419 tmax = 0x5e0000;
0420
0421
0422 wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),
0423 GFP_KERNEL);
0424 if (wf_smu_cpu_fans == NULL)
0425 goto fail;
0426 wf_smu_cpu_fans->ticks = 1;
0427
0428 wf_smu_cpu_fans->scale = WF_SMU_CPU_FANS_SIBLING_SCALE;
0429 wf_smu_cpu_fans->offset = WF_SMU_CPU_FANS_SIBLING_OFFSET;
0430
0431
0432 pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;
0433 pid_param.history_len = piddata->history_len;
0434 if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
0435 printk(KERN_WARNING "windfarm: History size overflow on "
0436 "CPU control loop (%d)\n", piddata->history_len);
0437 pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
0438 }
0439 pid_param.gd = piddata->gd;
0440 pid_param.gp = piddata->gp;
0441 pid_param.gr = piddata->gr / pid_param.history_len;
0442
0443 tdelta = ((s32)piddata->target_temp_delta) << 16;
0444 maxpow = ((s32)piddata->max_power) << 16;
0445 powadj = ((s32)piddata->power_adj) << 16;
0446
0447 pid_param.tmax = tmax;
0448 pid_param.ttarget = tmax - tdelta;
0449 pid_param.pmaxadj = maxpow - powadj;
0450
0451 pid_param.min = wf_control_get_min(fan_cpu_main);
0452 pid_param.max = wf_control_get_max(fan_cpu_main);
0453
0454 wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
0455
0456 DBG("wf: CPU Fan control initialized.\n");
0457 DBG(" ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",
0458 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
0459 pid_param.min, pid_param.max);
0460
0461 return;
0462
0463 fail:
0464 printk(KERN_WARNING "windfarm: CPU fan config not found\n"
0465 "for this machine model, max fan speed\n");
0466
0467 if (cpufreq_clamp)
0468 wf_control_set_max(cpufreq_clamp);
0469 if (fan_cpu_main)
0470 wf_control_set_max(fan_cpu_main);
0471 }
0472
0473 static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
0474 {
0475 s32 new_setpoint, temp, power, systarget;
0476 int rc;
0477
0478 if (--st->ticks != 0) {
0479 if (wf_smu_readjust)
0480 goto readjust;
0481 return;
0482 }
0483 st->ticks = WF_SMU_CPU_FANS_INTERVAL;
0484
0485 rc = wf_sensor_get(sensor_cpu_temp, &temp);
0486 if (rc) {
0487 printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
0488 rc);
0489 wf_smu_failure_state |= FAILURE_SENSOR;
0490 return;
0491 }
0492
0493 rc = wf_sensor_get(sensor_cpu_power, &power);
0494 if (rc) {
0495 printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
0496 rc);
0497 wf_smu_failure_state |= FAILURE_SENSOR;
0498 return;
0499 }
0500
0501 DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",
0502 FIX32TOPRINT(temp), FIX32TOPRINT(power));
0503
0504 #ifdef HACKED_OVERTEMP
0505 if (temp > 0x4a0000)
0506 wf_smu_failure_state |= FAILURE_OVERTEMP;
0507 #else
0508 if (temp > st->pid.param.tmax)
0509 wf_smu_failure_state |= FAILURE_OVERTEMP;
0510 #endif
0511 new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
0512
0513 DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
0514
0515 systarget = wf_smu_sys_fans ? wf_smu_sys_fans->pid.target : 0;
0516 systarget = ((((s64)systarget) * (s64)st->scale) >> 12)
0517 + st->offset;
0518 new_setpoint = max(new_setpoint, systarget);
0519 new_setpoint = max(new_setpoint, st->pid.param.min);
0520 new_setpoint = min(new_setpoint, st->pid.param.max);
0521
0522 DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)new_setpoint);
0523
0524 if (st->cpu_setpoint == new_setpoint)
0525 return;
0526 st->cpu_setpoint = new_setpoint;
0527 readjust:
0528 if (fan_cpu_main && wf_smu_failure_state == 0) {
0529 rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
0530 if (rc) {
0531 printk(KERN_WARNING "windfarm: CPU main fan"
0532 " error %d\n", rc);
0533 wf_smu_failure_state |= FAILURE_FAN;
0534 }
0535 }
0536 }
0537
0538
0539
0540
0541
0542
0543 static void wf_smu_tick(void)
0544 {
0545 unsigned int last_failure = wf_smu_failure_state;
0546 unsigned int new_failure;
0547
0548 if (!wf_smu_started) {
0549 DBG("wf: creating control loops !\n");
0550 wf_smu_create_sys_fans();
0551 wf_smu_create_cpu_fans();
0552 wf_smu_started = true;
0553 }
0554
0555
0556 if (wf_smu_skipping && --wf_smu_skipping)
0557 return;
0558
0559 wf_smu_failure_state = 0;
0560 if (wf_smu_sys_fans)
0561 wf_smu_sys_fans_tick(wf_smu_sys_fans);
0562 if (wf_smu_cpu_fans)
0563 wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
0564
0565 wf_smu_readjust = 0;
0566 new_failure = wf_smu_failure_state & ~last_failure;
0567
0568
0569
0570
0571 if (wf_smu_failure_state && !last_failure) {
0572 if (cpufreq_clamp)
0573 wf_control_set_max(cpufreq_clamp);
0574 if (fan_system)
0575 wf_control_set_max(fan_system);
0576 if (fan_cpu_main)
0577 wf_control_set_max(fan_cpu_main);
0578 if (fan_hd)
0579 wf_control_set_max(fan_hd);
0580 }
0581
0582
0583
0584
0585 if (!wf_smu_failure_state && last_failure) {
0586 if (cpufreq_clamp)
0587 wf_control_set_min(cpufreq_clamp);
0588 wf_smu_readjust = 1;
0589 }
0590
0591
0592
0593
0594 if (new_failure & FAILURE_OVERTEMP) {
0595 wf_set_overtemp();
0596 wf_smu_skipping = 2;
0597 wf_smu_overtemp = true;
0598 }
0599
0600
0601
0602
0603
0604
0605
0606 if (!wf_smu_failure_state && wf_smu_overtemp) {
0607 wf_clear_overtemp();
0608 wf_smu_overtemp = false;
0609 }
0610 }
0611
0612 static void wf_smu_new_control(struct wf_control *ct)
0613 {
0614 if (wf_smu_all_controls_ok)
0615 return;
0616
0617 if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) {
0618 if (wf_get_control(ct) == 0)
0619 fan_cpu_main = ct;
0620 }
0621
0622 if (fan_system == NULL && !strcmp(ct->name, "system-fan")) {
0623 if (wf_get_control(ct) == 0)
0624 fan_system = ct;
0625 }
0626
0627 if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
0628 if (wf_get_control(ct) == 0)
0629 cpufreq_clamp = ct;
0630 }
0631
0632
0633
0634
0635
0636 if (wf_smu_mach_model > 3) {
0637 if (fan_system && fan_cpu_main && cpufreq_clamp)
0638 wf_smu_all_controls_ok = 1;
0639 return;
0640 }
0641
0642 if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {
0643 if (wf_get_control(ct) == 0)
0644 fan_hd = ct;
0645 }
0646
0647 if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp)
0648 wf_smu_all_controls_ok = 1;
0649 }
0650
0651 static void wf_smu_new_sensor(struct wf_sensor *sr)
0652 {
0653 if (wf_smu_all_sensors_ok)
0654 return;
0655
0656 if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {
0657 if (wf_get_sensor(sr) == 0)
0658 sensor_cpu_power = sr;
0659 }
0660
0661 if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {
0662 if (wf_get_sensor(sr) == 0)
0663 sensor_cpu_temp = sr;
0664 }
0665
0666 if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {
0667 if (wf_get_sensor(sr) == 0)
0668 sensor_hd_temp = sr;
0669 }
0670
0671 if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp)
0672 wf_smu_all_sensors_ok = 1;
0673 }
0674
0675
0676 static int wf_smu_notify(struct notifier_block *self,
0677 unsigned long event, void *data)
0678 {
0679 switch(event) {
0680 case WF_EVENT_NEW_CONTROL:
0681 DBG("wf: new control %s detected\n",
0682 ((struct wf_control *)data)->name);
0683 wf_smu_new_control(data);
0684 wf_smu_readjust = 1;
0685 break;
0686 case WF_EVENT_NEW_SENSOR:
0687 DBG("wf: new sensor %s detected\n",
0688 ((struct wf_sensor *)data)->name);
0689 wf_smu_new_sensor(data);
0690 break;
0691 case WF_EVENT_TICK:
0692 if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)
0693 wf_smu_tick();
0694 }
0695
0696 return 0;
0697 }
0698
0699 static struct notifier_block wf_smu_events = {
0700 .notifier_call = wf_smu_notify,
0701 };
0702
0703 static int wf_init_pm(void)
0704 {
0705 const struct smu_sdbp_header *hdr;
0706
0707 hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
0708 if (hdr != 0) {
0709 struct smu_sdbp_sensortree *st =
0710 (struct smu_sdbp_sensortree *)&hdr[1];
0711 wf_smu_mach_model = st->model_id;
0712 }
0713
0714 printk(KERN_INFO "windfarm: Initializing for iMacG5 model ID %d\n",
0715 wf_smu_mach_model);
0716
0717 return 0;
0718 }
0719
0720 static int wf_smu_probe(struct platform_device *ddev)
0721 {
0722 wf_register_client(&wf_smu_events);
0723
0724 return 0;
0725 }
0726
0727 static int wf_smu_remove(struct platform_device *ddev)
0728 {
0729 wf_unregister_client(&wf_smu_events);
0730
0731
0732
0733
0734
0735 msleep(1000);
0736
0737
0738
0739
0740
0741
0742
0743
0744 if (sensor_cpu_power)
0745 wf_put_sensor(sensor_cpu_power);
0746 if (sensor_cpu_temp)
0747 wf_put_sensor(sensor_cpu_temp);
0748 if (sensor_hd_temp)
0749 wf_put_sensor(sensor_hd_temp);
0750
0751
0752 if (fan_cpu_main)
0753 wf_put_control(fan_cpu_main);
0754 if (fan_hd)
0755 wf_put_control(fan_hd);
0756 if (fan_system)
0757 wf_put_control(fan_system);
0758 if (cpufreq_clamp)
0759 wf_put_control(cpufreq_clamp);
0760
0761
0762 kfree(wf_smu_sys_fans);
0763 kfree(wf_smu_cpu_fans);
0764
0765 return 0;
0766 }
0767
0768 static struct platform_driver wf_smu_driver = {
0769 .probe = wf_smu_probe,
0770 .remove = wf_smu_remove,
0771 .driver = {
0772 .name = "windfarm",
0773 },
0774 };
0775
0776
0777 static int __init wf_smu_init(void)
0778 {
0779 int rc = -ENODEV;
0780
0781 if (of_machine_is_compatible("PowerMac8,1") ||
0782 of_machine_is_compatible("PowerMac8,2"))
0783 rc = wf_init_pm();
0784
0785 if (rc == 0) {
0786 #ifdef MODULE
0787 request_module("windfarm_smu_controls");
0788 request_module("windfarm_smu_sensors");
0789 request_module("windfarm_lm75_sensor");
0790 request_module("windfarm_cpufreq_clamp");
0791
0792 #endif
0793 platform_driver_register(&wf_smu_driver);
0794 }
0795
0796 return rc;
0797 }
0798
0799 static void __exit wf_smu_exit(void)
0800 {
0801
0802 platform_driver_unregister(&wf_smu_driver);
0803 }
0804
0805
0806 module_init(wf_smu_init);
0807 module_exit(wf_smu_exit);
0808
0809 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
0810 MODULE_DESCRIPTION("Thermal control logic for iMac G5");
0811 MODULE_LICENSE("GPL");
0812 MODULE_ALIAS("platform:windfarm");