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