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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202#undef DEBUG
203
204#include <linux/types.h>
205#include <linux/errno.h>
206#include <linux/kernel.h>
207#include <linux/delay.h>
208#include <linux/slab.h>
209#include <linux/init.h>
210#include <linux/spinlock.h>
211#include <linux/wait.h>
212#include <linux/kmod.h>
213#include <linux/device.h>
214#include <linux/platform_device.h>
215#include <asm/prom.h>
216#include <asm/machdep.h>
217#include <asm/io.h>
218#include <asm/sections.h>
219#include <asm/smu.h>
220
221#include "windfarm.h"
222#include "windfarm_pid.h"
223
224#define VERSION "0.3"
225
226static int pm121_mach_model;
227
228
229static struct wf_sensor *sensor_cpu_power;
230static struct wf_sensor *sensor_cpu_temp;
231static struct wf_sensor *sensor_cpu_voltage;
232static struct wf_sensor *sensor_cpu_current;
233static struct wf_sensor *sensor_gpu_temp;
234static struct wf_sensor *sensor_north_bridge_temp;
235static struct wf_sensor *sensor_hard_drive_temp;
236static struct wf_sensor *sensor_optical_drive_temp;
237static struct wf_sensor *sensor_incoming_air_temp;
238
239enum {
240 FAN_CPU,
241 FAN_HD,
242 FAN_OD,
243 CPUFREQ,
244 N_CONTROLS
245};
246static struct wf_control *controls[N_CONTROLS] = {};
247
248
249static int pm121_all_controls_ok, pm121_all_sensors_ok;
250static bool pm121_started;
251
252enum {
253 FAILURE_FAN = 1 << 0,
254 FAILURE_SENSOR = 1 << 1,
255 FAILURE_OVERTEMP = 1 << 2
256};
257
258
259
260enum {
261 LOOP_GPU,
262
263 LOOP_HD,
264 LOOP_KODIAK,
265 LOOP_OD,
266 N_LOOPS
267};
268
269static const char *loop_names[N_LOOPS] = {
270 "GPU",
271 "HD",
272 "KODIAK",
273 "OD",
274};
275
276#define PM121_NUM_CONFIGS 2
277
278static unsigned int pm121_failure_state;
279static int pm121_readjust, pm121_skipping;
280static bool pm121_overtemp;
281static s32 average_power;
282
283struct pm121_correction {
284 int offset;
285 int slope;
286};
287
288static struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
289
290 {
291
292 { .offset = -19563152,
293 .slope = 1956315
294 },
295
296 { .offset = -15650652,
297 .slope = 1565065
298 },
299 },
300
301 {
302
303 { .offset = -15650652,
304 .slope = 1565065
305 },
306
307 { .offset = -19563152,
308 .slope = 1956315
309 },
310 },
311
312 {
313
314 { .offset = -25431900,
315 .slope = 2543190
316 },
317
318 { .offset = -15650652,
319 .slope = 1565065
320 },
321 },
322
323};
324
325struct pm121_connection {
326 unsigned int control_id;
327 unsigned int ref_id;
328 struct pm121_correction correction;
329};
330
331static struct pm121_connection pm121_connections[] = {
332
333 { .control_id = FAN_CPU,
334 .ref_id = FAN_OD,
335 { .offset = -32768000,
336 .slope = 65536
337 }
338 },
339
340 { .control_id = FAN_OD,
341 .ref_id = FAN_HD,
342 { .offset = -32768000,
343 .slope = 65536
344 }
345 },
346};
347
348
349static struct pm121_connection *pm121_connection;
350
351
352
353
354
355
356
357
358
359
360
361struct pm121_sys_param {
362
363 int model_id;
364 struct wf_sensor **sensor;
365 s32 gp, itarget;
366 unsigned int control_id;
367};
368
369static struct pm121_sys_param
370pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
371
372 {
373 { .model_id = 2,
374 .sensor = &sensor_gpu_temp,
375 .gp = 0x002A6666,
376 .itarget = 0x5A0000,
377 .control_id = FAN_HD,
378 },
379 { .model_id = 3,
380 .sensor = &sensor_gpu_temp,
381 .gp = 0x0010CCCC,
382 .itarget = 0x500000,
383 .control_id = FAN_CPU,
384 },
385 },
386
387 {
388 { .model_id = 2,
389 .sensor = &sensor_hard_drive_temp,
390 .gp = 0x002D70A3,
391 .itarget = 0x370000,
392 .control_id = FAN_HD,
393 },
394 { .model_id = 3,
395 .sensor = &sensor_hard_drive_temp,
396 .gp = 0x002170A3,
397 .itarget = 0x370000,
398 .control_id = FAN_HD,
399 },
400 },
401
402 {
403 { .model_id = 2,
404 .sensor = &sensor_north_bridge_temp,
405 .gp = 0x003BD70A,
406 .itarget = 0x550000,
407 .control_id = FAN_OD,
408 },
409 { .model_id = 3,
410 .sensor = &sensor_north_bridge_temp,
411 .gp = 0x0030F5C2,
412 .itarget = 0x550000,
413 .control_id = FAN_HD,
414 },
415 },
416
417 {
418 { .model_id = 2,
419 .sensor = &sensor_optical_drive_temp,
420 .gp = 0x001FAE14,
421 .itarget = 0x320000,
422 .control_id = FAN_OD,
423 },
424 { .model_id = 3,
425 .sensor = &sensor_optical_drive_temp,
426 .gp = 0x001FAE14,
427 .itarget = 0x320000,
428 .control_id = FAN_OD,
429 },
430 },
431};
432
433
434#define PM121_SYS_GD 0x00000000
435#define PM121_SYS_GR 0x00019999
436#define PM121_SYS_HISTORY_SIZE 2
437#define PM121_SYS_INTERVAL 5
438
439
440
441struct pm121_sys_state {
442 int ticks;
443 s32 setpoint;
444 struct wf_pid_state pid;
445};
446
447struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
448
449
450
451
452
453
454#define PM121_CPU_INTERVAL 1
455
456
457
458struct pm121_cpu_state {
459 int ticks;
460 s32 setpoint;
461 struct wf_cpu_pid_state pid;
462};
463
464static struct pm121_cpu_state *pm121_cpu_state;
465
466
467
468
469
470
471
472
473
474static s32 pm121_correct(s32 new_setpoint,
475 unsigned int control_id,
476 s32 min)
477{
478 s32 new_min;
479 struct pm121_correction *correction;
480 correction = &corrections[control_id][pm121_mach_model - 2];
481
482 new_min = (average_power * correction->slope) >> 16;
483 new_min += correction->offset;
484 new_min = (new_min >> 16) + min;
485
486 return max3(new_setpoint, new_min, 0);
487}
488
489static s32 pm121_connect(unsigned int control_id, s32 setpoint)
490{
491 s32 new_min, value, new_setpoint;
492
493 if (pm121_connection->control_id == control_id) {
494 controls[control_id]->ops->get_value(controls[control_id],
495 &value);
496 new_min = value * pm121_connection->correction.slope;
497 new_min += pm121_connection->correction.offset;
498 if (new_min > 0) {
499 new_setpoint = max(setpoint, (new_min >> 16));
500 if (new_setpoint != setpoint) {
501 pr_debug("pm121: %s depending on %s, "
502 "corrected from %d to %d RPM\n",
503 controls[control_id]->name,
504 controls[pm121_connection->ref_id]->name,
505 (int) setpoint, (int) new_setpoint);
506 }
507 } else
508 new_setpoint = setpoint;
509 }
510
511 else
512 new_setpoint = setpoint;
513
514 return new_setpoint;
515}
516
517
518static void pm121_create_sys_fans(int loop_id)
519{
520 struct pm121_sys_param *param = NULL;
521 struct wf_pid_param pid_param;
522 struct wf_control *control = NULL;
523 int i;
524
525
526 for (i = 0; i < PM121_NUM_CONFIGS; i++) {
527 if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
528 param = &(pm121_sys_all_params[loop_id][i]);
529 break;
530 }
531 }
532
533
534 if (param == NULL) {
535 printk(KERN_WARNING "pm121: %s fan config not found "
536 " for this machine model\n",
537 loop_names[loop_id]);
538 goto fail;
539 }
540
541 control = controls[param->control_id];
542
543
544 pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
545 GFP_KERNEL);
546 if (pm121_sys_state[loop_id] == NULL) {
547 printk(KERN_WARNING "pm121: Memory allocation error\n");
548 goto fail;
549 }
550 pm121_sys_state[loop_id]->ticks = 1;
551
552
553 pid_param.gd = PM121_SYS_GD;
554 pid_param.gp = param->gp;
555 pid_param.gr = PM121_SYS_GR;
556 pid_param.interval = PM121_SYS_INTERVAL;
557 pid_param.history_len = PM121_SYS_HISTORY_SIZE;
558 pid_param.itarget = param->itarget;
559 if(control)
560 {
561 pid_param.min = control->ops->get_min(control);
562 pid_param.max = control->ops->get_max(control);
563 } else {
564
565
566
567
568 pid_param.min = 0;
569 pid_param.max = 0;
570 }
571
572 wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
573
574 pr_debug("pm121: %s Fan control loop initialized.\n"
575 " itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
576 loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
577 pid_param.min, pid_param.max);
578 return;
579
580 fail:
581
582
583 printk(KERN_WARNING "pm121: failed to set up %s loop "
584 "setting \"%s\" to max speed.\n",
585 loop_names[loop_id], control ? control->name : "uninitialized value");
586
587 if (control)
588 wf_control_set_max(control);
589}
590
591static void pm121_sys_fans_tick(int loop_id)
592{
593 struct pm121_sys_param *param;
594 struct pm121_sys_state *st;
595 struct wf_sensor *sensor;
596 struct wf_control *control;
597 s32 temp, new_setpoint;
598 int rc;
599
600 param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
601 st = pm121_sys_state[loop_id];
602 sensor = *(param->sensor);
603 control = controls[param->control_id];
604
605 if (--st->ticks != 0) {
606 if (pm121_readjust)
607 goto readjust;
608 return;
609 }
610 st->ticks = PM121_SYS_INTERVAL;
611
612 rc = sensor->ops->get_value(sensor, &temp);
613 if (rc) {
614 printk(KERN_WARNING "windfarm: %s sensor error %d\n",
615 sensor->name, rc);
616 pm121_failure_state |= FAILURE_SENSOR;
617 return;
618 }
619
620 pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",
621 loop_names[loop_id], sensor->name,
622 FIX32TOPRINT(temp));
623
624 new_setpoint = wf_pid_run(&st->pid, temp);
625
626
627 new_setpoint = pm121_correct(new_setpoint,
628 param->control_id,
629 st->pid.param.min);
630
631 new_setpoint = pm121_connect(param->control_id, new_setpoint);
632
633 if (new_setpoint == st->setpoint)
634 return;
635 st->setpoint = new_setpoint;
636 pr_debug("pm121: %s corrected setpoint: %d RPM\n",
637 control->name, (int)new_setpoint);
638 readjust:
639 if (control && pm121_failure_state == 0) {
640 rc = control->ops->set_value(control, st->setpoint);
641 if (rc) {
642 printk(KERN_WARNING "windfarm: %s fan error %d\n",
643 control->name, rc);
644 pm121_failure_state |= FAILURE_FAN;
645 }
646 }
647}
648
649
650
651static void pm121_create_cpu_fans(void)
652{
653 struct wf_cpu_pid_param pid_param;
654 const struct smu_sdbp_header *hdr;
655 struct smu_sdbp_cpupiddata *piddata;
656 struct smu_sdbp_fvt *fvt;
657 struct wf_control *fan_cpu;
658 s32 tmax, tdelta, maxpow, powadj;
659
660 fan_cpu = controls[FAN_CPU];
661
662
663 hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
664 if (hdr == 0) {
665 printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
666 goto fail;
667 }
668 piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
669
670
671
672
673 hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
674 if (hdr) {
675 fvt = (struct smu_sdbp_fvt *)&hdr[1];
676 tmax = ((s32)fvt->maxtemp) << 16;
677 } else
678 tmax = 0x5e0000;
679
680
681 pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
682 GFP_KERNEL);
683 if (pm121_cpu_state == NULL)
684 goto fail;
685 pm121_cpu_state->ticks = 1;
686
687
688 pid_param.interval = PM121_CPU_INTERVAL;
689 pid_param.history_len = piddata->history_len;
690 if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
691 printk(KERN_WARNING "pm121: History size overflow on "
692 "CPU control loop (%d)\n", piddata->history_len);
693 pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
694 }
695 pid_param.gd = piddata->gd;
696 pid_param.gp = piddata->gp;
697 pid_param.gr = piddata->gr / pid_param.history_len;
698
699 tdelta = ((s32)piddata->target_temp_delta) << 16;
700 maxpow = ((s32)piddata->max_power) << 16;
701 powadj = ((s32)piddata->power_adj) << 16;
702
703 pid_param.tmax = tmax;
704 pid_param.ttarget = tmax - tdelta;
705 pid_param.pmaxadj = maxpow - powadj;
706
707 pid_param.min = fan_cpu->ops->get_min(fan_cpu);
708 pid_param.max = fan_cpu->ops->get_max(fan_cpu);
709
710 wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
711
712 pr_debug("pm121: CPU Fan control initialized.\n");
713 pr_debug(" ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
714 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
715 pid_param.min, pid_param.max);
716
717 return;
718
719 fail:
720 printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
721
722 if (controls[CPUFREQ])
723 wf_control_set_max(controls[CPUFREQ]);
724 if (fan_cpu)
725 wf_control_set_max(fan_cpu);
726}
727
728
729static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
730{
731 s32 new_setpoint, temp, power;
732 struct wf_control *fan_cpu = NULL;
733 int rc;
734
735 if (--st->ticks != 0) {
736 if (pm121_readjust)
737 goto readjust;
738 return;
739 }
740 st->ticks = PM121_CPU_INTERVAL;
741
742 fan_cpu = controls[FAN_CPU];
743
744 rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
745 if (rc) {
746 printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
747 rc);
748 pm121_failure_state |= FAILURE_SENSOR;
749 return;
750 }
751
752 rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
753 if (rc) {
754 printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
755 rc);
756 pm121_failure_state |= FAILURE_SENSOR;
757 return;
758 }
759
760 pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
761 FIX32TOPRINT(temp), FIX32TOPRINT(power));
762
763 if (temp > st->pid.param.tmax)
764 pm121_failure_state |= FAILURE_OVERTEMP;
765
766 new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
767
768
769 new_setpoint = pm121_correct(new_setpoint,
770 FAN_CPU,
771 st->pid.param.min);
772
773
774 new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
775
776 if (st->setpoint == new_setpoint)
777 return;
778 st->setpoint = new_setpoint;
779 pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
780
781 readjust:
782 if (fan_cpu && pm121_failure_state == 0) {
783 rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
784 if (rc) {
785 printk(KERN_WARNING "pm121: %s fan error %d\n",
786 fan_cpu->name, rc);
787 pm121_failure_state |= FAILURE_FAN;
788 }
789 }
790}
791
792
793
794
795
796
797static void pm121_tick(void)
798{
799 unsigned int last_failure = pm121_failure_state;
800 unsigned int new_failure;
801 s32 total_power;
802 int i;
803
804 if (!pm121_started) {
805 pr_debug("pm121: creating control loops !\n");
806 for (i = 0; i < N_LOOPS; i++)
807 pm121_create_sys_fans(i);
808
809 pm121_create_cpu_fans();
810 pm121_started = true;
811 }
812
813
814 if (pm121_skipping && --pm121_skipping)
815 return;
816
817
818 total_power = 0;
819 for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
820 total_power += pm121_cpu_state->pid.powers[i];
821
822 average_power = total_power / pm121_cpu_state->pid.param.history_len;
823
824
825 pm121_failure_state = 0;
826 for (i = 0 ; i < N_LOOPS; i++) {
827 if (pm121_sys_state[i])
828 pm121_sys_fans_tick(i);
829 }
830
831 if (pm121_cpu_state)
832 pm121_cpu_fans_tick(pm121_cpu_state);
833
834 pm121_readjust = 0;
835 new_failure = pm121_failure_state & ~last_failure;
836
837
838
839
840 if (pm121_failure_state && !last_failure) {
841 for (i = 0; i < N_CONTROLS; i++) {
842 if (controls[i])
843 wf_control_set_max(controls[i]);
844 }
845 }
846
847
848
849
850 if (!pm121_failure_state && last_failure) {
851 if (controls[CPUFREQ])
852 wf_control_set_min(controls[CPUFREQ]);
853 pm121_readjust = 1;
854 }
855
856
857
858
859 if (new_failure & FAILURE_OVERTEMP) {
860 wf_set_overtemp();
861 pm121_skipping = 2;
862 pm121_overtemp = true;
863 }
864
865
866
867
868
869
870
871 if (!pm121_failure_state && pm121_overtemp) {
872 wf_clear_overtemp();
873 pm121_overtemp = false;
874 }
875}
876
877
878static struct wf_control* pm121_register_control(struct wf_control *ct,
879 const char *match,
880 unsigned int id)
881{
882 if (controls[id] == NULL && !strcmp(ct->name, match)) {
883 if (wf_get_control(ct) == 0)
884 controls[id] = ct;
885 }
886 return controls[id];
887}
888
889static void pm121_new_control(struct wf_control *ct)
890{
891 int all = 1;
892
893 if (pm121_all_controls_ok)
894 return;
895
896 all = pm121_register_control(ct, "optical-drive-fan", FAN_OD) && all;
897 all = pm121_register_control(ct, "hard-drive-fan", FAN_HD) && all;
898 all = pm121_register_control(ct, "cpu-fan", FAN_CPU) && all;
899 all = pm121_register_control(ct, "cpufreq-clamp", CPUFREQ) && all;
900
901 if (all)
902 pm121_all_controls_ok = 1;
903}
904
905
906
907
908static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,
909 const char *match,
910 struct wf_sensor **var)
911{
912 if (*var == NULL && !strcmp(sensor->name, match)) {
913 if (wf_get_sensor(sensor) == 0)
914 *var = sensor;
915 }
916 return *var;
917}
918
919static void pm121_new_sensor(struct wf_sensor *sr)
920{
921 int all = 1;
922
923 if (pm121_all_sensors_ok)
924 return;
925
926 all = pm121_register_sensor(sr, "cpu-temp",
927 &sensor_cpu_temp) && all;
928 all = pm121_register_sensor(sr, "cpu-current",
929 &sensor_cpu_current) && all;
930 all = pm121_register_sensor(sr, "cpu-voltage",
931 &sensor_cpu_voltage) && all;
932 all = pm121_register_sensor(sr, "cpu-power",
933 &sensor_cpu_power) && all;
934 all = pm121_register_sensor(sr, "hard-drive-temp",
935 &sensor_hard_drive_temp) && all;
936 all = pm121_register_sensor(sr, "optical-drive-temp",
937 &sensor_optical_drive_temp) && all;
938 all = pm121_register_sensor(sr, "incoming-air-temp",
939 &sensor_incoming_air_temp) && all;
940 all = pm121_register_sensor(sr, "north-bridge-temp",
941 &sensor_north_bridge_temp) && all;
942 all = pm121_register_sensor(sr, "gpu-temp",
943 &sensor_gpu_temp) && all;
944
945 if (all)
946 pm121_all_sensors_ok = 1;
947}
948
949
950
951static int pm121_notify(struct notifier_block *self,
952 unsigned long event, void *data)
953{
954 switch (event) {
955 case WF_EVENT_NEW_CONTROL:
956 pr_debug("pm121: new control %s detected\n",
957 ((struct wf_control *)data)->name);
958 pm121_new_control(data);
959 break;
960 case WF_EVENT_NEW_SENSOR:
961 pr_debug("pm121: new sensor %s detected\n",
962 ((struct wf_sensor *)data)->name);
963 pm121_new_sensor(data);
964 break;
965 case WF_EVENT_TICK:
966 if (pm121_all_controls_ok && pm121_all_sensors_ok)
967 pm121_tick();
968 break;
969 }
970
971 return 0;
972}
973
974static struct notifier_block pm121_events = {
975 .notifier_call = pm121_notify,
976};
977
978static int pm121_init_pm(void)
979{
980 const struct smu_sdbp_header *hdr;
981
982 hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
983 if (hdr != 0) {
984 struct smu_sdbp_sensortree *st =
985 (struct smu_sdbp_sensortree *)&hdr[1];
986 pm121_mach_model = st->model_id;
987 }
988
989 pm121_connection = &pm121_connections[pm121_mach_model - 2];
990
991 printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
992 pm121_mach_model);
993
994 return 0;
995}
996
997
998static int pm121_probe(struct platform_device *ddev)
999{
1000 wf_register_client(&pm121_events);
1001
1002 return 0;
1003}
1004
1005static int pm121_remove(struct platform_device *ddev)
1006{
1007 wf_unregister_client(&pm121_events);
1008 return 0;
1009}
1010
1011static struct platform_driver pm121_driver = {
1012 .probe = pm121_probe,
1013 .remove = pm121_remove,
1014 .driver = {
1015 .name = "windfarm",
1016 .bus = &platform_bus_type,
1017 },
1018};
1019
1020
1021static int __init pm121_init(void)
1022{
1023 int rc = -ENODEV;
1024
1025 if (of_machine_is_compatible("PowerMac12,1"))
1026 rc = pm121_init_pm();
1027
1028 if (rc == 0) {
1029 request_module("windfarm_smu_controls");
1030 request_module("windfarm_smu_sensors");
1031 request_module("windfarm_smu_sat");
1032 request_module("windfarm_lm75_sensor");
1033 request_module("windfarm_max6690_sensor");
1034 request_module("windfarm_cpufreq_clamp");
1035 platform_driver_register(&pm121_driver);
1036 }
1037
1038 return rc;
1039}
1040
1041static void __exit pm121_exit(void)
1042{
1043
1044 platform_driver_unregister(&pm121_driver);
1045}
1046
1047
1048module_init(pm121_init);
1049module_exit(pm121_exit);
1050
1051MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
1052MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
1053MODULE_LICENSE("GPL");
1054
1055