diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /drivers/macintosh | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'drivers/macintosh')
37 files changed, 701 insertions, 3319 deletions
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index a555da64224..fa51af11c6f 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig | |||
@@ -204,14 +204,11 @@ config THERM_ADT746X | |||
204 | better fan behaviour by default, and some manual control. | 204 | better fan behaviour by default, and some manual control. |
205 | 205 | ||
206 | config THERM_PM72 | 206 | config THERM_PM72 |
207 | tristate "Support for thermal management on PowerMac G5 (AGP)" | 207 | tristate "Support for thermal management on PowerMac G5" |
208 | depends on I2C && I2C_POWERMAC && PPC_PMAC64 | 208 | depends on I2C && I2C_POWERMAC && PPC_PMAC64 |
209 | default n | ||
210 | help | 209 | help |
211 | This driver provides thermostat and fan control for the desktop | 210 | This driver provides thermostat and fan control for the desktop |
212 | G5 machines. | 211 | G5 machines. |
213 | |||
214 | This is deprecated, use windfarm instead. | ||
215 | 212 | ||
216 | config WINDFARM | 213 | config WINDFARM |
217 | tristate "New PowerMac thermal control infrastructure" | 214 | tristate "New PowerMac thermal control infrastructure" |
@@ -224,22 +221,6 @@ config WINDFARM_PM81 | |||
224 | help | 221 | help |
225 | This driver provides thermal control for the iMacG5 | 222 | This driver provides thermal control for the iMacG5 |
226 | 223 | ||
227 | config WINDFARM_PM72 | ||
228 | tristate "Support for thermal management on PowerMac G5 (AGP)" | ||
229 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU | ||
230 | select I2C_POWERMAC | ||
231 | help | ||
232 | This driver provides thermal control for the PowerMac G5 | ||
233 | "AGP" variants (PowerMac 7,2 and 7,3) | ||
234 | |||
235 | config WINDFARM_RM31 | ||
236 | tristate "Support for thermal management on Xserve G5" | ||
237 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU | ||
238 | select I2C_POWERMAC | ||
239 | help | ||
240 | This driver provides thermal control for the Xserve G5 | ||
241 | (RackMac3,1) | ||
242 | |||
243 | config WINDFARM_PM91 | 224 | config WINDFARM_PM91 |
244 | tristate "Support for thermal management on PowerMac9,1" | 225 | tristate "Support for thermal management on PowerMac9,1" |
245 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU | 226 | depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU |
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index 6753b65f8ed..6652a6ebb6f 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile | |||
@@ -29,20 +29,6 @@ obj-$(CONFIG_THERM_PM72) += therm_pm72.o | |||
29 | obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o | 29 | obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o |
30 | obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o | 30 | obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o |
31 | obj-$(CONFIG_WINDFARM) += windfarm_core.o | 31 | obj-$(CONFIG_WINDFARM) += windfarm_core.o |
32 | obj-$(CONFIG_WINDFARM_PM72) += windfarm_fcu_controls.o \ | ||
33 | windfarm_ad7417_sensor.o \ | ||
34 | windfarm_lm75_sensor.o \ | ||
35 | windfarm_max6690_sensor.o \ | ||
36 | windfarm_pid.o \ | ||
37 | windfarm_cpufreq_clamp.o \ | ||
38 | windfarm_pm72.o | ||
39 | obj-$(CONFIG_WINDFARM_RM31) += windfarm_fcu_controls.o \ | ||
40 | windfarm_ad7417_sensor.o \ | ||
41 | windfarm_lm75_sensor.o \ | ||
42 | windfarm_lm87_sensor.o \ | ||
43 | windfarm_pid.o \ | ||
44 | windfarm_cpufreq_clamp.o \ | ||
45 | windfarm_rm31.o | ||
46 | obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \ | 32 | obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \ |
47 | windfarm_smu_sensors.o \ | 33 | windfarm_smu_sensors.o \ |
48 | windfarm_lm75_sensor.o windfarm_pid.o \ | 34 | windfarm_lm75_sensor.o windfarm_pid.o \ |
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index b026896206c..75049e76519 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c | |||
@@ -710,7 +710,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, | |||
710 | req = NULL; | 710 | req = NULL; |
711 | spin_lock_irqsave(&state->lock, flags); | 711 | spin_lock_irqsave(&state->lock, flags); |
712 | add_wait_queue(&state->wait_queue, &wait); | 712 | add_wait_queue(&state->wait_queue, &wait); |
713 | set_current_state(TASK_INTERRUPTIBLE); | 713 | current->state = TASK_INTERRUPTIBLE; |
714 | 714 | ||
715 | for (;;) { | 715 | for (;;) { |
716 | req = state->completed; | 716 | req = state->completed; |
@@ -734,7 +734,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, | |||
734 | spin_lock_irqsave(&state->lock, flags); | 734 | spin_lock_irqsave(&state->lock, flags); |
735 | } | 735 | } |
736 | 736 | ||
737 | set_current_state(TASK_RUNNING); | 737 | current->state = TASK_RUNNING; |
738 | remove_wait_queue(&state->wait_queue, &wait); | 738 | remove_wait_queue(&state->wait_queue, &wait); |
739 | spin_unlock_irqrestore(&state->lock, flags); | 739 | spin_unlock_irqrestore(&state->lock, flags); |
740 | 740 | ||
diff --git a/drivers/macintosh/ams/ams-core.c b/drivers/macintosh/ams/ams-core.c index 36a4fdddd64..399beb1638d 100644 --- a/drivers/macintosh/ams/ams-core.c +++ b/drivers/macintosh/ams/ams-core.c | |||
@@ -31,7 +31,7 @@ | |||
31 | /* There is only one motion sensor per machine */ | 31 | /* There is only one motion sensor per machine */ |
32 | struct ams ams_info; | 32 | struct ams ams_info; |
33 | 33 | ||
34 | static bool verbose; | 34 | static unsigned int verbose; |
35 | module_param(verbose, bool, 0644); | 35 | module_param(verbose, bool, 0644); |
36 | MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output"); | 36 | MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output"); |
37 | 37 | ||
@@ -226,7 +226,7 @@ void ams_sensor_detach(void) | |||
226 | * We do this after ams_info.exit(), because an interrupt might | 226 | * We do this after ams_info.exit(), because an interrupt might |
227 | * have arrived before disabling them. | 227 | * have arrived before disabling them. |
228 | */ | 228 | */ |
229 | flush_work(&ams_info.worker); | 229 | flush_work_sync(&ams_info.worker); |
230 | 230 | ||
231 | /* Remove device */ | 231 | /* Remove device */ |
232 | of_device_unregister(ams_info.of_dev); | 232 | of_device_unregister(ams_info.of_dev); |
diff --git a/drivers/macintosh/ams/ams-i2c.c b/drivers/macintosh/ams/ams-i2c.c index 978eda8d667..abeecd27b48 100644 --- a/drivers/macintosh/ams/ams-i2c.c +++ b/drivers/macintosh/ams/ams-i2c.c | |||
@@ -65,7 +65,7 @@ static int ams_i2c_probe(struct i2c_client *client, | |||
65 | static int ams_i2c_remove(struct i2c_client *client); | 65 | static int ams_i2c_remove(struct i2c_client *client); |
66 | 66 | ||
67 | static const struct i2c_device_id ams_id[] = { | 67 | static const struct i2c_device_id ams_id[] = { |
68 | { "MAC,accelerometer_1", 0 }, | 68 | { "ams", 0 }, |
69 | { } | 69 | { } |
70 | }; | 70 | }; |
71 | MODULE_DEVICE_TABLE(i2c, ams_id); | 71 | MODULE_DEVICE_TABLE(i2c, ams_id); |
diff --git a/drivers/macintosh/ams/ams-input.c b/drivers/macintosh/ams/ams-input.c index b27e530a87a..8a712392cd3 100644 --- a/drivers/macintosh/ams/ams-input.c +++ b/drivers/macintosh/ams/ams-input.c | |||
@@ -19,11 +19,11 @@ | |||
19 | 19 | ||
20 | #include "ams.h" | 20 | #include "ams.h" |
21 | 21 | ||
22 | static bool joystick; | 22 | static unsigned int joystick; |
23 | module_param(joystick, bool, S_IRUGO); | 23 | module_param(joystick, bool, S_IRUGO); |
24 | MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); | 24 | MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); |
25 | 25 | ||
26 | static bool invert; | 26 | static unsigned int invert; |
27 | module_param(invert, bool, S_IWUSR | S_IRUGO); | 27 | module_param(invert, bool, S_IWUSR | S_IRUGO); |
28 | MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); | 28 | MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); |
29 | 29 | ||
diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c index 87de8d9bcfa..b6ef8f59076 100644 --- a/drivers/macintosh/macio-adb.c +++ b/drivers/macintosh/macio-adb.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <asm/pgtable.h> | 14 | #include <asm/pgtable.h> |
15 | #include <asm/hydra.h> | 15 | #include <asm/hydra.h> |
16 | #include <asm/irq.h> | 16 | #include <asm/irq.h> |
17 | #include <asm/system.h> | ||
17 | #include <linux/init.h> | 18 | #include <linux/init.h> |
18 | #include <linux/ioport.h> | 19 | #include <linux/ioport.h> |
19 | 20 | ||
diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index ac5c8793986..4daf9e5a773 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c | |||
@@ -137,7 +137,7 @@ extern struct device_attribute macio_dev_attrs[]; | |||
137 | struct bus_type macio_bus_type = { | 137 | struct bus_type macio_bus_type = { |
138 | .name = "macio", | 138 | .name = "macio", |
139 | .match = macio_bus_match, | 139 | .match = macio_bus_match, |
140 | .uevent = of_device_uevent_modalias, | 140 | .uevent = of_device_uevent, |
141 | .probe = macio_device_probe, | 141 | .probe = macio_device_probe, |
142 | .remove = macio_device_remove, | 142 | .remove = macio_device_remove, |
143 | .shutdown = macio_device_shutdown, | 143 | .shutdown = macio_device_shutdown, |
@@ -679,7 +679,7 @@ void macio_release_resources(struct macio_dev *dev) | |||
679 | 679 | ||
680 | #ifdef CONFIG_PCI | 680 | #ifdef CONFIG_PCI |
681 | 681 | ||
682 | static int macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | 682 | static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
683 | { | 683 | { |
684 | struct device_node* np; | 684 | struct device_node* np; |
685 | struct macio_chip* chip; | 685 | struct macio_chip* chip; |
@@ -739,7 +739,7 @@ static int macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent | |||
739 | return 0; | 739 | return 0; |
740 | } | 740 | } |
741 | 741 | ||
742 | static void macio_pci_remove(struct pci_dev* pdev) | 742 | static void __devexit macio_pci_remove(struct pci_dev* pdev) |
743 | { | 743 | { |
744 | panic("removing of macio-asic not supported !\n"); | 744 | panic("removing of macio-asic not supported !\n"); |
745 | } | 745 | } |
@@ -748,7 +748,7 @@ static void macio_pci_remove(struct pci_dev* pdev) | |||
748 | * MacIO is matched against any Apple ID, it's probe() function | 748 | * MacIO is matched against any Apple ID, it's probe() function |
749 | * will then decide wether it applies or not | 749 | * will then decide wether it applies or not |
750 | */ | 750 | */ |
751 | static const struct pci_device_id pci_ids[] = { { | 751 | static const struct pci_device_id __devinitdata pci_ids [] = { { |
752 | .vendor = PCI_VENDOR_ID_APPLE, | 752 | .vendor = PCI_VENDOR_ID_APPLE, |
753 | .device = PCI_ANY_ID, | 753 | .device = PCI_ANY_ID, |
754 | .subvendor = PCI_ANY_ID, | 754 | .subvendor = PCI_ANY_ID, |
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index d98e566a8f5..2fd435bc542 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c | |||
@@ -63,7 +63,7 @@ struct media_bay_info { | |||
63 | int value_count; | 63 | int value_count; |
64 | int timer; | 64 | int timer; |
65 | struct macio_dev *mdev; | 65 | struct macio_dev *mdev; |
66 | const struct mb_ops* ops; | 66 | struct mb_ops* ops; |
67 | int index; | 67 | int index; |
68 | int cached_gpio; | 68 | int cached_gpio; |
69 | int sleeping; | 69 | int sleeping; |
@@ -356,7 +356,7 @@ static void poll_media_bay(struct media_bay_info* bay) | |||
356 | static char *mb_content_types[] = { | 356 | static char *mb_content_types[] = { |
357 | "a floppy drive", | 357 | "a floppy drive", |
358 | "a floppy drive", | 358 | "a floppy drive", |
359 | "an unsupported audio device", | 359 | "an unsuported audio device", |
360 | "an ATA device", | 360 | "an ATA device", |
361 | "an unsupported PCI device", | 361 | "an unsupported PCI device", |
362 | "an unknown device", | 362 | "an unknown device", |
@@ -556,8 +556,7 @@ static int media_bay_task(void *x) | |||
556 | return 0; | 556 | return 0; |
557 | } | 557 | } |
558 | 558 | ||
559 | static int media_bay_attach(struct macio_dev *mdev, | 559 | static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_device_id *match) |
560 | const struct of_device_id *match) | ||
561 | { | 560 | { |
562 | struct media_bay_info* bay; | 561 | struct media_bay_info* bay; |
563 | u32 __iomem *regbase; | 562 | u32 __iomem *regbase; |
@@ -670,7 +669,7 @@ static int media_bay_resume(struct macio_dev *mdev) | |||
670 | 669 | ||
671 | /* Definitions of "ops" structures. | 670 | /* Definitions of "ops" structures. |
672 | */ | 671 | */ |
673 | static const struct mb_ops ohare_mb_ops = { | 672 | static struct mb_ops ohare_mb_ops = { |
674 | .name = "Ohare", | 673 | .name = "Ohare", |
675 | .content = ohare_mb_content, | 674 | .content = ohare_mb_content, |
676 | .power = ohare_mb_power, | 675 | .power = ohare_mb_power, |
@@ -679,7 +678,7 @@ static const struct mb_ops ohare_mb_ops = { | |||
679 | .un_reset_ide = ohare_mb_un_reset_ide, | 678 | .un_reset_ide = ohare_mb_un_reset_ide, |
680 | }; | 679 | }; |
681 | 680 | ||
682 | static const struct mb_ops heathrow_mb_ops = { | 681 | static struct mb_ops heathrow_mb_ops = { |
683 | .name = "Heathrow", | 682 | .name = "Heathrow", |
684 | .content = heathrow_mb_content, | 683 | .content = heathrow_mb_content, |
685 | .power = heathrow_mb_power, | 684 | .power = heathrow_mb_power, |
@@ -688,7 +687,7 @@ static const struct mb_ops heathrow_mb_ops = { | |||
688 | .un_reset_ide = heathrow_mb_un_reset_ide, | 687 | .un_reset_ide = heathrow_mb_un_reset_ide, |
689 | }; | 688 | }; |
690 | 689 | ||
691 | static const struct mb_ops keylargo_mb_ops = { | 690 | static struct mb_ops keylargo_mb_ops = { |
692 | .name = "KeyLargo", | 691 | .name = "KeyLargo", |
693 | .init = keylargo_mb_init, | 692 | .init = keylargo_mb_init, |
694 | .content = keylargo_mb_content, | 693 | .content = keylargo_mb_content, |
diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index cad0e19b47a..2637c139777 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c | |||
@@ -81,13 +81,13 @@ static int rackmeter_ignore_nice; | |||
81 | */ | 81 | */ |
82 | static inline cputime64_t get_cpu_idle_time(unsigned int cpu) | 82 | static inline cputime64_t get_cpu_idle_time(unsigned int cpu) |
83 | { | 83 | { |
84 | u64 retval; | 84 | cputime64_t retval; |
85 | 85 | ||
86 | retval = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE] + | 86 | retval = cputime64_add(kstat_cpu(cpu).cpustat.idle, |
87 | kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; | 87 | kstat_cpu(cpu).cpustat.iowait); |
88 | 88 | ||
89 | if (rackmeter_ignore_nice) | 89 | if (rackmeter_ignore_nice) |
90 | retval += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; | 90 | retval = cputime64_add(retval, kstat_cpu(cpu).cpustat.nice); |
91 | 91 | ||
92 | return retval; | 92 | return retval; |
93 | } | 93 | } |
@@ -220,11 +220,13 @@ static void rackmeter_do_timer(struct work_struct *work) | |||
220 | int i, offset, load, cumm, pause; | 220 | int i, offset, load, cumm, pause; |
221 | 221 | ||
222 | cur_jiffies = jiffies64_to_cputime64(get_jiffies_64()); | 222 | cur_jiffies = jiffies64_to_cputime64(get_jiffies_64()); |
223 | total_ticks = (unsigned int) (cur_jiffies - rcpu->prev_wall); | 223 | total_ticks = (unsigned int)cputime64_sub(cur_jiffies, |
224 | rcpu->prev_wall); | ||
224 | rcpu->prev_wall = cur_jiffies; | 225 | rcpu->prev_wall = cur_jiffies; |
225 | 226 | ||
226 | total_idle_ticks = get_cpu_idle_time(cpu); | 227 | total_idle_ticks = get_cpu_idle_time(cpu); |
227 | idle_ticks = (unsigned int) (total_idle_ticks - rcpu->prev_idle); | 228 | idle_ticks = (unsigned int) cputime64_sub(total_idle_ticks, |
229 | rcpu->prev_idle); | ||
228 | rcpu->prev_idle = total_idle_ticks; | 230 | rcpu->prev_idle = total_idle_ticks; |
229 | 231 | ||
230 | /* We do a very dumb calculation to update the LEDs for now, | 232 | /* We do a very dumb calculation to update the LEDs for now, |
@@ -253,7 +255,7 @@ static void rackmeter_do_timer(struct work_struct *work) | |||
253 | msecs_to_jiffies(CPU_SAMPLING_RATE)); | 255 | msecs_to_jiffies(CPU_SAMPLING_RATE)); |
254 | } | 256 | } |
255 | 257 | ||
256 | static void rackmeter_init_cpu_sniffer(struct rackmeter *rm) | 258 | static void __devinit rackmeter_init_cpu_sniffer(struct rackmeter *rm) |
257 | { | 259 | { |
258 | unsigned int cpu; | 260 | unsigned int cpu; |
259 | 261 | ||
@@ -287,7 +289,7 @@ static void rackmeter_stop_cpu_sniffer(struct rackmeter *rm) | |||
287 | cancel_delayed_work_sync(&rm->cpu[1].sniffer); | 289 | cancel_delayed_work_sync(&rm->cpu[1].sniffer); |
288 | } | 290 | } |
289 | 291 | ||
290 | static int rackmeter_setup(struct rackmeter *rm) | 292 | static int __devinit rackmeter_setup(struct rackmeter *rm) |
291 | { | 293 | { |
292 | pr_debug("rackmeter: setting up i2s..\n"); | 294 | pr_debug("rackmeter: setting up i2s..\n"); |
293 | rackmeter_setup_i2s(rm); | 295 | rackmeter_setup_i2s(rm); |
@@ -362,8 +364,8 @@ static irqreturn_t rackmeter_irq(int irq, void *arg) | |||
362 | return IRQ_HANDLED; | 364 | return IRQ_HANDLED; |
363 | } | 365 | } |
364 | 366 | ||
365 | static int rackmeter_probe(struct macio_dev* mdev, | 367 | static int __devinit rackmeter_probe(struct macio_dev* mdev, |
366 | const struct of_device_id *match) | 368 | const struct of_device_id *match) |
367 | { | 369 | { |
368 | struct device_node *i2s = NULL, *np = NULL; | 370 | struct device_node *i2s = NULL, *np = NULL; |
369 | struct rackmeter *rm = NULL; | 371 | struct rackmeter *rm = NULL; |
@@ -521,7 +523,7 @@ static int rackmeter_probe(struct macio_dev* mdev, | |||
521 | return rc; | 523 | return rc; |
522 | } | 524 | } |
523 | 525 | ||
524 | static int rackmeter_remove(struct macio_dev* mdev) | 526 | static int __devexit rackmeter_remove(struct macio_dev* mdev) |
525 | { | 527 | { |
526 | struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev); | 528 | struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev); |
527 | 529 | ||
@@ -588,7 +590,7 @@ static struct macio_driver rackmeter_driver = { | |||
588 | .of_match_table = rackmeter_match, | 590 | .of_match_table = rackmeter_match, |
589 | }, | 591 | }, |
590 | .probe = rackmeter_probe, | 592 | .probe = rackmeter_probe, |
591 | .remove = rackmeter_remove, | 593 | .remove = __devexit_p(rackmeter_remove), |
592 | .shutdown = rackmeter_shutdown, | 594 | .shutdown = rackmeter_shutdown, |
593 | }; | 595 | }; |
594 | 596 | ||
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 9c6b9641486..116a49ce74b 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/completion.h> | 32 | #include <linux/completion.h> |
33 | #include <linux/miscdevice.h> | 33 | #include <linux/miscdevice.h> |
34 | #include <linux/delay.h> | 34 | #include <linux/delay.h> |
35 | #include <linux/sysdev.h> | ||
35 | #include <linux/poll.h> | 36 | #include <linux/poll.h> |
36 | #include <linux/mutex.h> | 37 | #include <linux/mutex.h> |
37 | #include <linux/of_device.h> | 38 | #include <linux/of_device.h> |
@@ -45,6 +46,7 @@ | |||
45 | #include <asm/pmac_feature.h> | 46 | #include <asm/pmac_feature.h> |
46 | #include <asm/smu.h> | 47 | #include <asm/smu.h> |
47 | #include <asm/sections.h> | 48 | #include <asm/sections.h> |
49 | #include <asm/abs_addr.h> | ||
48 | #include <asm/uaccess.h> | 50 | #include <asm/uaccess.h> |
49 | 51 | ||
50 | #define VERSION "0.7" | 52 | #define VERSION "0.7" |
@@ -501,7 +503,7 @@ int __init smu_init (void) | |||
501 | * 32 bits value safely | 503 | * 32 bits value safely |
502 | */ | 504 | */ |
503 | smu->cmd_buf_abs = (u32)smu_cmdbuf_abs; | 505 | smu->cmd_buf_abs = (u32)smu_cmdbuf_abs; |
504 | smu->cmd_buf = __va(smu_cmdbuf_abs); | 506 | smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs); |
505 | 507 | ||
506 | smu->db_node = of_find_node_by_name(NULL, "smu-doorbell"); | 508 | smu->db_node = of_find_node_by_name(NULL, "smu-doorbell"); |
507 | if (smu->db_node == NULL) { | 509 | if (smu->db_node == NULL) { |
@@ -565,7 +567,7 @@ fail_msg_node: | |||
565 | fail_db_node: | 567 | fail_db_node: |
566 | of_node_put(smu->db_node); | 568 | of_node_put(smu->db_node); |
567 | fail_bootmem: | 569 | fail_bootmem: |
568 | free_bootmem(__pa(smu), sizeof(struct smu_device)); | 570 | free_bootmem((unsigned long)smu, sizeof(struct smu_device)); |
569 | smu = NULL; | 571 | smu = NULL; |
570 | fail_np: | 572 | fail_np: |
571 | of_node_put(np); | 573 | of_node_put(np); |
@@ -679,6 +681,9 @@ static struct platform_driver smu_of_platform_driver = | |||
679 | static int __init smu_init_sysfs(void) | 681 | static int __init smu_init_sysfs(void) |
680 | { | 682 | { |
681 | /* | 683 | /* |
684 | * Due to sysfs bogosity, a sysdev is not a real device, so | ||
685 | * we should in fact create both if we want sysdev semantics | ||
686 | * for power management. | ||
682 | * For now, we don't power manage machines with an SMU chip, | 687 | * For now, we don't power manage machines with an SMU chip, |
683 | * I'm a bit too far from figuring out how that works with those | 688 | * I'm a bit too far from figuring out how that works with those |
684 | * new chipsets, but that will come back and bite us | 689 | * new chipsets, but that will come back and bite us |
@@ -997,7 +1002,7 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id) | |||
997 | "%02x !\n", id, hdr->id); | 1002 | "%02x !\n", id, hdr->id); |
998 | goto failure; | 1003 | goto failure; |
999 | } | 1004 | } |
1000 | if (of_add_property(smu->of_node, prop)) { | 1005 | if (prom_add_property(smu->of_node, prop)) { |
1001 | printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " | 1006 | printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " |
1002 | "property !\n", id); | 1007 | "property !\n", id); |
1003 | goto failure; | 1008 | goto failure; |
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index f433521a6f9..02367308ff2 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <asm/prom.h> | 29 | #include <asm/prom.h> |
30 | #include <asm/machdep.h> | 30 | #include <asm/machdep.h> |
31 | #include <asm/io.h> | 31 | #include <asm/io.h> |
32 | #include <asm/system.h> | ||
32 | #include <asm/sections.h> | 33 | #include <asm/sections.h> |
33 | 34 | ||
34 | #undef DEBUG | 35 | #undef DEBUG |
@@ -47,11 +48,11 @@ static u8 FAN_SPD_SET[2] = {0x30, 0x31}; | |||
47 | 48 | ||
48 | static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ | 49 | static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ |
49 | static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ | 50 | static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ |
50 | static const char *sensor_location[3] = { "?", "?", "?" }; | 51 | static const char *sensor_location[3]; |
51 | 52 | ||
52 | static int limit_adjust; | 53 | static int limit_adjust; |
53 | static int fan_speed = -1; | 54 | static int fan_speed = -1; |
54 | static bool verbose; | 55 | static int verbose; |
55 | 56 | ||
56 | MODULE_AUTHOR("Colin Leroy <colin@colino.net>"); | 57 | MODULE_AUTHOR("Colin Leroy <colin@colino.net>"); |
57 | MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and " | 58 | MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and " |
@@ -79,16 +80,18 @@ struct thermostat { | |||
79 | int last_speed[2]; | 80 | int last_speed[2]; |
80 | int last_var[2]; | 81 | int last_var[2]; |
81 | int pwm_inv[2]; | 82 | int pwm_inv[2]; |
82 | struct task_struct *thread; | ||
83 | struct platform_device *pdev; | ||
84 | enum { | ||
85 | ADT7460, | ||
86 | ADT7467 | ||
87 | } type; | ||
88 | }; | 83 | }; |
89 | 84 | ||
85 | static enum {ADT7460, ADT7467} therm_type; | ||
86 | static int therm_bus, therm_address; | ||
87 | static struct platform_device * of_dev; | ||
88 | static struct thermostat* thermostat; | ||
89 | static struct task_struct *thread_therm = NULL; | ||
90 | |||
90 | static void write_both_fan_speed(struct thermostat *th, int speed); | 91 | static void write_both_fan_speed(struct thermostat *th, int speed); |
91 | static void write_fan_speed(struct thermostat *th, int speed, int fan); | 92 | static void write_fan_speed(struct thermostat *th, int speed, int fan); |
93 | static void thermostat_create_files(void); | ||
94 | static void thermostat_remove_files(void); | ||
92 | 95 | ||
93 | static int | 96 | static int |
94 | write_reg(struct thermostat* th, int reg, u8 data) | 97 | write_reg(struct thermostat* th, int reg, u8 data) |
@@ -124,6 +127,66 @@ read_reg(struct thermostat* th, int reg) | |||
124 | return data; | 127 | return data; |
125 | } | 128 | } |
126 | 129 | ||
130 | static struct i2c_driver thermostat_driver; | ||
131 | |||
132 | static int | ||
133 | attach_thermostat(struct i2c_adapter *adapter) | ||
134 | { | ||
135 | unsigned long bus_no; | ||
136 | struct i2c_board_info info; | ||
137 | struct i2c_client *client; | ||
138 | |||
139 | if (strncmp(adapter->name, "uni-n", 5)) | ||
140 | return -ENODEV; | ||
141 | bus_no = simple_strtoul(adapter->name + 6, NULL, 10); | ||
142 | if (bus_no != therm_bus) | ||
143 | return -ENODEV; | ||
144 | |||
145 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
146 | strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE); | ||
147 | info.addr = therm_address; | ||
148 | client = i2c_new_device(adapter, &info); | ||
149 | if (!client) | ||
150 | return -ENODEV; | ||
151 | |||
152 | /* | ||
153 | * Let i2c-core delete that device on driver removal. | ||
154 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
155 | */ | ||
156 | list_add_tail(&client->detected, &thermostat_driver.clients); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int | ||
161 | remove_thermostat(struct i2c_client *client) | ||
162 | { | ||
163 | struct thermostat *th = i2c_get_clientdata(client); | ||
164 | int i; | ||
165 | |||
166 | thermostat_remove_files(); | ||
167 | |||
168 | if (thread_therm != NULL) { | ||
169 | kthread_stop(thread_therm); | ||
170 | } | ||
171 | |||
172 | printk(KERN_INFO "adt746x: Putting max temperatures back from " | ||
173 | "%d, %d, %d to %d, %d, %d\n", | ||
174 | th->limits[0], th->limits[1], th->limits[2], | ||
175 | th->initial_limits[0], th->initial_limits[1], | ||
176 | th->initial_limits[2]); | ||
177 | |||
178 | for (i = 0; i < 3; i++) | ||
179 | write_reg(th, LIMIT_REG[i], th->initial_limits[i]); | ||
180 | |||
181 | write_both_fan_speed(th, -1); | ||
182 | |||
183 | thermostat = NULL; | ||
184 | |||
185 | kfree(th); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
127 | static int read_fan_speed(struct thermostat *th, u8 addr) | 190 | static int read_fan_speed(struct thermostat *th, u8 addr) |
128 | { | 191 | { |
129 | u8 tmp[2]; | 192 | u8 tmp[2]; |
@@ -141,7 +204,7 @@ static int read_fan_speed(struct thermostat *th, u8 addr) | |||
141 | static void write_both_fan_speed(struct thermostat *th, int speed) | 204 | static void write_both_fan_speed(struct thermostat *th, int speed) |
142 | { | 205 | { |
143 | write_fan_speed(th, speed, 0); | 206 | write_fan_speed(th, speed, 0); |
144 | if (th->type == ADT7460) | 207 | if (therm_type == ADT7460) |
145 | write_fan_speed(th, speed, 1); | 208 | write_fan_speed(th, speed, 1); |
146 | } | 209 | } |
147 | 210 | ||
@@ -154,7 +217,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) | |||
154 | else if (speed < -1) | 217 | else if (speed < -1) |
155 | speed = 0; | 218 | speed = 0; |
156 | 219 | ||
157 | if (th->type == ADT7467 && fan == 1) | 220 | if (therm_type == ADT7467 && fan == 1) |
158 | return; | 221 | return; |
159 | 222 | ||
160 | if (th->last_speed[fan] != speed) { | 223 | if (th->last_speed[fan] != speed) { |
@@ -177,7 +240,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) | |||
177 | write_reg(th, FAN_SPD_SET[fan], speed); | 240 | write_reg(th, FAN_SPD_SET[fan], speed); |
178 | } else { | 241 | } else { |
179 | /* back to automatic */ | 242 | /* back to automatic */ |
180 | if(th->type == ADT7460) { | 243 | if(therm_type == ADT7460) { |
181 | manual = read_reg(th, | 244 | manual = read_reg(th, |
182 | MANUAL_MODE[fan]) & (~MANUAL_MASK); | 245 | MANUAL_MODE[fan]) & (~MANUAL_MASK); |
183 | manual &= ~INVERT_MASK; | 246 | manual &= ~INVERT_MASK; |
@@ -231,7 +294,7 @@ static void update_fans_speed (struct thermostat *th) | |||
231 | /* we don't care about local sensor, so we start at sensor 1 */ | 294 | /* we don't care about local sensor, so we start at sensor 1 */ |
232 | for (i = 1; i < 3; i++) { | 295 | for (i = 1; i < 3; i++) { |
233 | int started = 0; | 296 | int started = 0; |
234 | int fan_number = (th->type == ADT7460 && i == 2); | 297 | int fan_number = (therm_type == ADT7460 && i == 2); |
235 | int var = th->temps[i] - th->limits[i]; | 298 | int var = th->temps[i] - th->limits[i]; |
236 | 299 | ||
237 | if (var > -1) { | 300 | if (var > -1) { |
@@ -308,22 +371,116 @@ static int monitor_task(void *arg) | |||
308 | 371 | ||
309 | static void set_limit(struct thermostat *th, int i) | 372 | static void set_limit(struct thermostat *th, int i) |
310 | { | 373 | { |
311 | /* Set sensor1 limit higher to avoid powerdowns */ | 374 | /* Set sensor1 limit higher to avoid powerdowns */ |
312 | th->limits[i] = default_limits_chip[i] + limit_adjust; | 375 | th->limits[i] = default_limits_chip[i] + limit_adjust; |
313 | write_reg(th, LIMIT_REG[i], th->limits[i]); | 376 | write_reg(th, LIMIT_REG[i], th->limits[i]); |
314 | 377 | ||
315 | /* set our limits to normal */ | 378 | /* set our limits to normal */ |
316 | th->limits[i] = default_limits_local[i] + limit_adjust; | 379 | th->limits[i] = default_limits_local[i] + limit_adjust; |
317 | } | 380 | } |
318 | 381 | ||
319 | #define BUILD_SHOW_FUNC_INT(name, data) \ | 382 | static int probe_thermostat(struct i2c_client *client, |
320 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 383 | const struct i2c_device_id *id) |
321 | { \ | 384 | { |
322 | struct thermostat *th = dev_get_drvdata(dev); \ | 385 | struct thermostat* th; |
323 | return sprintf(buf, "%d\n", data); \ | 386 | int rc; |
387 | int i; | ||
388 | |||
389 | if (thermostat) | ||
390 | return 0; | ||
391 | |||
392 | th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); | ||
393 | if (!th) | ||
394 | return -ENOMEM; | ||
395 | |||
396 | i2c_set_clientdata(client, th); | ||
397 | th->clt = client; | ||
398 | |||
399 | rc = read_reg(th, CONFIG_REG); | ||
400 | if (rc < 0) { | ||
401 | dev_err(&client->dev, "Thermostat failed to read config!\n"); | ||
402 | kfree(th); | ||
403 | return -ENODEV; | ||
404 | } | ||
405 | |||
406 | /* force manual control to start the fan quieter */ | ||
407 | if (fan_speed == -1) | ||
408 | fan_speed = 64; | ||
409 | |||
410 | if(therm_type == ADT7460) { | ||
411 | printk(KERN_INFO "adt746x: ADT7460 initializing\n"); | ||
412 | /* The 7460 needs to be started explicitly */ | ||
413 | write_reg(th, CONFIG_REG, 1); | ||
414 | } else | ||
415 | printk(KERN_INFO "adt746x: ADT7467 initializing\n"); | ||
416 | |||
417 | for (i = 0; i < 3; i++) { | ||
418 | th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); | ||
419 | set_limit(th, i); | ||
420 | } | ||
421 | |||
422 | printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" | ||
423 | " to %d, %d, %d\n", | ||
424 | th->initial_limits[0], th->initial_limits[1], | ||
425 | th->initial_limits[2], th->limits[0], th->limits[1], | ||
426 | th->limits[2]); | ||
427 | |||
428 | thermostat = th; | ||
429 | |||
430 | /* record invert bit status because fw can corrupt it after suspend */ | ||
431 | th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; | ||
432 | th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; | ||
433 | |||
434 | /* be sure to really write fan speed the first time */ | ||
435 | th->last_speed[0] = -2; | ||
436 | th->last_speed[1] = -2; | ||
437 | th->last_var[0] = -80; | ||
438 | th->last_var[1] = -80; | ||
439 | |||
440 | if (fan_speed != -1) { | ||
441 | /* manual mode, stop fans */ | ||
442 | write_both_fan_speed(th, 0); | ||
443 | } else { | ||
444 | /* automatic mode */ | ||
445 | write_both_fan_speed(th, -1); | ||
446 | } | ||
447 | |||
448 | thread_therm = kthread_run(monitor_task, th, "kfand"); | ||
449 | |||
450 | if (thread_therm == ERR_PTR(-ENOMEM)) { | ||
451 | printk(KERN_INFO "adt746x: Kthread creation failed\n"); | ||
452 | thread_therm = NULL; | ||
453 | return -ENOMEM; | ||
454 | } | ||
455 | |||
456 | thermostat_create_files(); | ||
457 | |||
458 | return 0; | ||
324 | } | 459 | } |
325 | 460 | ||
326 | #define BUILD_SHOW_FUNC_INT_LITE(name, data) \ | 461 | static const struct i2c_device_id therm_adt746x_id[] = { |
462 | { "therm_adt746x", 0 }, | ||
463 | { } | ||
464 | }; | ||
465 | |||
466 | static struct i2c_driver thermostat_driver = { | ||
467 | .driver = { | ||
468 | .name = "therm_adt746x", | ||
469 | }, | ||
470 | .attach_adapter = attach_thermostat, | ||
471 | .probe = probe_thermostat, | ||
472 | .remove = remove_thermostat, | ||
473 | .id_table = therm_adt746x_id, | ||
474 | }; | ||
475 | |||
476 | /* | ||
477 | * Now, unfortunately, sysfs doesn't give us a nice void * we could | ||
478 | * pass around to the attribute functions, so we don't really have | ||
479 | * choice but implement a bunch of them... | ||
480 | * | ||
481 | * FIXME, it does now... | ||
482 | */ | ||
483 | #define BUILD_SHOW_FUNC_INT(name, data) \ | ||
327 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 484 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ |
328 | { \ | 485 | { \ |
329 | return sprintf(buf, "%d\n", data); \ | 486 | return sprintf(buf, "%d\n", data); \ |
@@ -338,24 +495,22 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch | |||
338 | #define BUILD_SHOW_FUNC_FAN(name, data) \ | 495 | #define BUILD_SHOW_FUNC_FAN(name, data) \ |
339 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 496 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ |
340 | { \ | 497 | { \ |
341 | struct thermostat *th = dev_get_drvdata(dev); \ | ||
342 | return sprintf(buf, "%d (%d rpm)\n", \ | 498 | return sprintf(buf, "%d (%d rpm)\n", \ |
343 | th->last_speed[data], \ | 499 | thermostat->last_speed[data], \ |
344 | read_fan_speed(th, FAN_SPEED[data]) \ | 500 | read_fan_speed(thermostat, FAN_SPEED[data]) \ |
345 | ); \ | 501 | ); \ |
346 | } | 502 | } |
347 | 503 | ||
348 | #define BUILD_STORE_FUNC_DEG(name, data) \ | 504 | #define BUILD_STORE_FUNC_DEG(name, data) \ |
349 | static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ | 505 | static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ |
350 | { \ | 506 | { \ |
351 | struct thermostat *th = dev_get_drvdata(dev); \ | ||
352 | int val; \ | 507 | int val; \ |
353 | int i; \ | 508 | int i; \ |
354 | val = simple_strtol(buf, NULL, 10); \ | 509 | val = simple_strtol(buf, NULL, 10); \ |
355 | printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ | 510 | printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ |
356 | limit_adjust = val; \ | 511 | limit_adjust = val; \ |
357 | for (i=0; i < 3; i++) \ | 512 | for (i=0; i < 3; i++) \ |
358 | set_limit(th, i); \ | 513 | set_limit(thermostat, i); \ |
359 | return n; \ | 514 | return n; \ |
360 | } | 515 | } |
361 | 516 | ||
@@ -371,21 +526,20 @@ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, c | |||
371 | return n; \ | 526 | return n; \ |
372 | } | 527 | } |
373 | 528 | ||
374 | BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(th, TEMP_REG[1]))) | 529 | BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(thermostat, TEMP_REG[1]))) |
375 | BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(th, TEMP_REG[2]))) | 530 | BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(thermostat, TEMP_REG[2]))) |
376 | BUILD_SHOW_FUNC_INT(sensor1_limit, th->limits[1]) | 531 | BUILD_SHOW_FUNC_INT(sensor1_limit, thermostat->limits[1]) |
377 | BUILD_SHOW_FUNC_INT(sensor2_limit, th->limits[2]) | 532 | BUILD_SHOW_FUNC_INT(sensor2_limit, thermostat->limits[2]) |
378 | BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) | 533 | BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) |
379 | BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) | 534 | BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) |
380 | 535 | ||
381 | BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed) | 536 | BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed) |
382 | BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) | ||
383 | |||
384 | BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) | 537 | BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) |
385 | BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) | 538 | BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) |
386 | 539 | ||
387 | BUILD_SHOW_FUNC_INT_LITE(limit_adjust, limit_adjust) | 540 | BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) |
388 | BUILD_STORE_FUNC_DEG(limit_adjust, th) | 541 | BUILD_SHOW_FUNC_INT(limit_adjust, limit_adjust) |
542 | BUILD_STORE_FUNC_DEG(limit_adjust, thermostat) | ||
389 | 543 | ||
390 | static DEVICE_ATTR(sensor1_temperature, S_IRUGO, | 544 | static DEVICE_ATTR(sensor1_temperature, S_IRUGO, |
391 | show_sensor1_temperature,NULL); | 545 | show_sensor1_temperature,NULL); |
@@ -411,77 +565,53 @@ static DEVICE_ATTR(sensor2_fan_speed, S_IRUGO, | |||
411 | static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, | 565 | static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, |
412 | show_limit_adjust, store_limit_adjust); | 566 | show_limit_adjust, store_limit_adjust); |
413 | 567 | ||
414 | static void thermostat_create_files(struct thermostat *th) | 568 | |
569 | static int __init | ||
570 | thermostat_init(void) | ||
415 | { | 571 | { |
416 | struct device_node *np = th->clt->dev.of_node; | 572 | struct device_node* np; |
417 | struct device *dev; | 573 | const u32 *prop; |
418 | int err; | 574 | int i = 0, offset = 0; |
419 | 575 | ||
420 | /* To maintain ABI compatibility with userspace, create | 576 | np = of_find_node_by_name(NULL, "fan"); |
421 | * the old style platform driver and attach the attributes | 577 | if (!np) |
422 | * to it here | 578 | return -ENODEV; |
423 | */ | 579 | if (of_device_is_compatible(np, "adt7460")) |
424 | th->pdev = of_platform_device_create(np, "temperatures", NULL); | 580 | therm_type = ADT7460; |
425 | if (!th->pdev) | 581 | else if (of_device_is_compatible(np, "adt7467")) |
426 | return; | 582 | therm_type = ADT7467; |
427 | dev = &th->pdev->dev; | 583 | else { |
428 | dev_set_drvdata(dev, th); | 584 | of_node_put(np); |
429 | err = device_create_file(dev, &dev_attr_sensor1_temperature); | 585 | return -ENODEV; |
430 | err |= device_create_file(dev, &dev_attr_sensor2_temperature); | 586 | } |
431 | err |= device_create_file(dev, &dev_attr_sensor1_limit); | ||
432 | err |= device_create_file(dev, &dev_attr_sensor2_limit); | ||
433 | err |= device_create_file(dev, &dev_attr_sensor1_location); | ||
434 | err |= device_create_file(dev, &dev_attr_sensor2_location); | ||
435 | err |= device_create_file(dev, &dev_attr_limit_adjust); | ||
436 | err |= device_create_file(dev, &dev_attr_specified_fan_speed); | ||
437 | err |= device_create_file(dev, &dev_attr_sensor1_fan_speed); | ||
438 | if(th->type == ADT7460) | ||
439 | err |= device_create_file(dev, &dev_attr_sensor2_fan_speed); | ||
440 | if (err) | ||
441 | printk(KERN_WARNING | ||
442 | "Failed to create temperature attribute file(s).\n"); | ||
443 | } | ||
444 | 587 | ||
445 | static void thermostat_remove_files(struct thermostat *th) | 588 | prop = of_get_property(np, "hwsensor-params-version", NULL); |
446 | { | 589 | printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, |
447 | struct device *dev; | 590 | (*prop == 1)?"":"un"); |
591 | if (*prop != 1) { | ||
592 | of_node_put(np); | ||
593 | return -ENODEV; | ||
594 | } | ||
448 | 595 | ||
449 | if (!th->pdev) | 596 | prop = of_get_property(np, "reg", NULL); |
450 | return; | 597 | if (!prop) { |
451 | dev = &th->pdev->dev; | 598 | of_node_put(np); |
452 | device_remove_file(dev, &dev_attr_sensor1_temperature); | 599 | return -ENODEV; |
453 | device_remove_file(dev, &dev_attr_sensor2_temperature); | 600 | } |
454 | device_remove_file(dev, &dev_attr_sensor1_limit); | ||
455 | device_remove_file(dev, &dev_attr_sensor2_limit); | ||
456 | device_remove_file(dev, &dev_attr_sensor1_location); | ||
457 | device_remove_file(dev, &dev_attr_sensor2_location); | ||
458 | device_remove_file(dev, &dev_attr_limit_adjust); | ||
459 | device_remove_file(dev, &dev_attr_specified_fan_speed); | ||
460 | device_remove_file(dev, &dev_attr_sensor1_fan_speed); | ||
461 | if (th->type == ADT7460) | ||
462 | device_remove_file(dev, &dev_attr_sensor2_fan_speed); | ||
463 | of_device_unregister(th->pdev); | ||
464 | 601 | ||
465 | } | 602 | /* look for bus either by path or using "reg" */ |
603 | if (strstr(np->full_name, "/i2c-bus@") != NULL) { | ||
604 | const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9); | ||
605 | therm_bus = tmp_bus[0]-'0'; | ||
606 | } else { | ||
607 | therm_bus = ((*prop) >> 8) & 0x0f; | ||
608 | } | ||
466 | 609 | ||
467 | static int probe_thermostat(struct i2c_client *client, | 610 | therm_address = ((*prop) & 0xff) >> 1; |
468 | const struct i2c_device_id *id) | ||
469 | { | ||
470 | struct device_node *np = client->dev.of_node; | ||
471 | struct thermostat* th; | ||
472 | const __be32 *prop; | ||
473 | int i, rc, vers, offset = 0; | ||
474 | 611 | ||
475 | if (!np) | 612 | printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, " |
476 | return -ENXIO; | 613 | "limit_adjust: %d, fan_speed: %d\n", |
477 | prop = of_get_property(np, "hwsensor-params-version", NULL); | 614 | therm_bus, therm_address, limit_adjust, fan_speed); |
478 | if (!prop) | ||
479 | return -ENXIO; | ||
480 | vers = be32_to_cpup(prop); | ||
481 | printk(KERN_INFO "adt746x: version %d (%ssupported)\n", | ||
482 | vers, vers == 1 ? "" : "un"); | ||
483 | if (vers != 1) | ||
484 | return -ENXIO; | ||
485 | 615 | ||
486 | if (of_get_property(np, "hwsensor-location", NULL)) { | 616 | if (of_get_property(np, "hwsensor-location", NULL)) { |
487 | for (i = 0; i < 3; i++) { | 617 | for (i = 0; i < 3; i++) { |
@@ -494,129 +624,72 @@ static int probe_thermostat(struct i2c_client *client, | |||
494 | printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); | 624 | printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); |
495 | offset += strlen(sensor_location[i]) + 1; | 625 | offset += strlen(sensor_location[i]) + 1; |
496 | } | 626 | } |
627 | } else { | ||
628 | sensor_location[0] = "?"; | ||
629 | sensor_location[1] = "?"; | ||
630 | sensor_location[2] = "?"; | ||
497 | } | 631 | } |
498 | 632 | ||
499 | th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); | 633 | of_dev = of_platform_device_create(np, "temperatures", NULL); |
500 | if (!th) | 634 | of_node_put(np); |
501 | return -ENOMEM; | ||
502 | |||
503 | i2c_set_clientdata(client, th); | ||
504 | th->clt = client; | ||
505 | th->type = id->driver_data; | ||
506 | 635 | ||
507 | rc = read_reg(th, CONFIG_REG); | 636 | if (of_dev == NULL) { |
508 | if (rc < 0) { | 637 | printk(KERN_ERR "Can't register temperatures device !\n"); |
509 | dev_err(&client->dev, "Thermostat failed to read config!\n"); | ||
510 | kfree(th); | ||
511 | return -ENODEV; | 638 | return -ENODEV; |
512 | } | 639 | } |
513 | 640 | ||
514 | /* force manual control to start the fan quieter */ | 641 | #ifndef CONFIG_I2C_POWERMAC |
515 | if (fan_speed == -1) | 642 | request_module("i2c-powermac"); |
516 | fan_speed = 64; | 643 | #endif |
517 | |||
518 | if (th->type == ADT7460) { | ||
519 | printk(KERN_INFO "adt746x: ADT7460 initializing\n"); | ||
520 | /* The 7460 needs to be started explicitly */ | ||
521 | write_reg(th, CONFIG_REG, 1); | ||
522 | } else | ||
523 | printk(KERN_INFO "adt746x: ADT7467 initializing\n"); | ||
524 | |||
525 | for (i = 0; i < 3; i++) { | ||
526 | th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); | ||
527 | set_limit(th, i); | ||
528 | } | ||
529 | |||
530 | printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" | ||
531 | " to %d, %d, %d\n", | ||
532 | th->initial_limits[0], th->initial_limits[1], | ||
533 | th->initial_limits[2], th->limits[0], th->limits[1], | ||
534 | th->limits[2]); | ||
535 | |||
536 | /* record invert bit status because fw can corrupt it after suspend */ | ||
537 | th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; | ||
538 | th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; | ||
539 | |||
540 | /* be sure to really write fan speed the first time */ | ||
541 | th->last_speed[0] = -2; | ||
542 | th->last_speed[1] = -2; | ||
543 | th->last_var[0] = -80; | ||
544 | th->last_var[1] = -80; | ||
545 | |||
546 | if (fan_speed != -1) { | ||
547 | /* manual mode, stop fans */ | ||
548 | write_both_fan_speed(th, 0); | ||
549 | } else { | ||
550 | /* automatic mode */ | ||
551 | write_both_fan_speed(th, -1); | ||
552 | } | ||
553 | |||
554 | th->thread = kthread_run(monitor_task, th, "kfand"); | ||
555 | if (th->thread == ERR_PTR(-ENOMEM)) { | ||
556 | printk(KERN_INFO "adt746x: Kthread creation failed\n"); | ||
557 | th->thread = NULL; | ||
558 | return -ENOMEM; | ||
559 | } | ||
560 | |||
561 | thermostat_create_files(th); | ||
562 | 644 | ||
563 | return 0; | 645 | return i2c_add_driver(&thermostat_driver); |
564 | } | 646 | } |
565 | 647 | ||
566 | static int remove_thermostat(struct i2c_client *client) | 648 | static void thermostat_create_files(void) |
567 | { | 649 | { |
568 | struct thermostat *th = i2c_get_clientdata(client); | 650 | int err; |
569 | int i; | ||
570 | |||
571 | thermostat_remove_files(th); | ||
572 | |||
573 | if (th->thread != NULL) | ||
574 | kthread_stop(th->thread); | ||
575 | |||
576 | printk(KERN_INFO "adt746x: Putting max temperatures back from " | ||
577 | "%d, %d, %d to %d, %d, %d\n", | ||
578 | th->limits[0], th->limits[1], th->limits[2], | ||
579 | th->initial_limits[0], th->initial_limits[1], | ||
580 | th->initial_limits[2]); | ||
581 | |||
582 | for (i = 0; i < 3; i++) | ||
583 | write_reg(th, LIMIT_REG[i], th->initial_limits[i]); | ||
584 | |||
585 | write_both_fan_speed(th, -1); | ||
586 | |||
587 | kfree(th); | ||
588 | 651 | ||
589 | return 0; | 652 | err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature); |
653 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature); | ||
654 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit); | ||
655 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_limit); | ||
656 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_location); | ||
657 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_location); | ||
658 | err |= device_create_file(&of_dev->dev, &dev_attr_limit_adjust); | ||
659 | err |= device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed); | ||
660 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); | ||
661 | if(therm_type == ADT7460) | ||
662 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed); | ||
663 | if (err) | ||
664 | printk(KERN_WARNING | ||
665 | "Failed to create temperature attribute file(s).\n"); | ||
590 | } | 666 | } |
591 | 667 | ||
592 | static const struct i2c_device_id therm_adt746x_id[] = { | 668 | static void thermostat_remove_files(void) |
593 | { "MAC,adt7460", ADT7460 }, | ||
594 | { "MAC,adt7467", ADT7467 }, | ||
595 | { } | ||
596 | }; | ||
597 | MODULE_DEVICE_TABLE(i2c, therm_adt746x_id); | ||
598 | |||
599 | static struct i2c_driver thermostat_driver = { | ||
600 | .driver = { | ||
601 | .name = "therm_adt746x", | ||
602 | }, | ||
603 | .probe = probe_thermostat, | ||
604 | .remove = remove_thermostat, | ||
605 | .id_table = therm_adt746x_id, | ||
606 | }; | ||
607 | |||
608 | static int __init thermostat_init(void) | ||
609 | { | 669 | { |
610 | #ifndef CONFIG_I2C_POWERMAC | 670 | if (of_dev) { |
611 | request_module("i2c-powermac"); | 671 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature); |
612 | #endif | 672 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature); |
673 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit); | ||
674 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit); | ||
675 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_location); | ||
676 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_location); | ||
677 | device_remove_file(&of_dev->dev, &dev_attr_limit_adjust); | ||
678 | device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed); | ||
679 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); | ||
680 | |||
681 | if(therm_type == ADT7460) | ||
682 | device_remove_file(&of_dev->dev, | ||
683 | &dev_attr_sensor2_fan_speed); | ||
613 | 684 | ||
614 | return i2c_add_driver(&thermostat_driver); | 685 | } |
615 | } | 686 | } |
616 | 687 | ||
617 | static void __exit thermostat_exit(void) | 688 | static void __exit |
689 | thermostat_exit(void) | ||
618 | { | 690 | { |
619 | i2c_del_driver(&thermostat_driver); | 691 | i2c_del_driver(&thermostat_driver); |
692 | of_device_unregister(of_dev); | ||
620 | } | 693 | } |
621 | 694 | ||
622 | module_init(thermostat_init); | 695 | module_init(thermostat_init); |
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 97cfc5ac9fd..0ff92c20800 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c | |||
@@ -127,6 +127,7 @@ | |||
127 | #include <asm/prom.h> | 127 | #include <asm/prom.h> |
128 | #include <asm/machdep.h> | 128 | #include <asm/machdep.h> |
129 | #include <asm/io.h> | 129 | #include <asm/io.h> |
130 | #include <asm/system.h> | ||
130 | #include <asm/sections.h> | 131 | #include <asm/sections.h> |
131 | #include <asm/macio.h> | 132 | #include <asm/macio.h> |
132 | 133 | ||
diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index 3b4a157714b..46c4e95f10d 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <asm/prom.h> | 41 | #include <asm/prom.h> |
42 | #include <asm/machdep.h> | 42 | #include <asm/machdep.h> |
43 | #include <asm/io.h> | 43 | #include <asm/io.h> |
44 | #include <asm/system.h> | ||
44 | #include <asm/sections.h> | 45 | #include <asm/sections.h> |
45 | #include <asm/macio.h> | 46 | #include <asm/macio.h> |
46 | 47 | ||
diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index 86511c570dd..971bc9582a5 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <asm/mac_via.h> | 26 | #include <asm/mac_via.h> |
27 | #endif | 27 | #endif |
28 | #include <asm/io.h> | 28 | #include <asm/io.h> |
29 | #include <asm/system.h> | ||
29 | #include <linux/init.h> | 30 | #include <linux/init.h> |
30 | 31 | ||
31 | static volatile unsigned char __iomem *via; | 32 | static volatile unsigned char __iomem *via; |
diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 3725f088f17..817f37a875c 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <asm/macintosh.h> | 34 | #include <asm/macintosh.h> |
35 | #include <asm/macints.h> | 35 | #include <asm/macints.h> |
36 | #include <asm/mac_via.h> | 36 | #include <asm/mac_via.h> |
37 | #include <asm/system.h> | ||
37 | 38 | ||
38 | static volatile unsigned char *via; | 39 | static volatile unsigned char *via; |
39 | 40 | ||
@@ -158,7 +159,7 @@ int macii_init(void) | |||
158 | err = macii_init_via(); | 159 | err = macii_init_via(); |
159 | if (err) goto out; | 160 | if (err) goto out; |
160 | 161 | ||
161 | err = request_irq(IRQ_MAC_ADB, macii_interrupt, 0, "ADB", | 162 | err = request_irq(IRQ_MAC_ADB, macii_interrupt, IRQ_FLG_LOCK, "ADB", |
162 | macii_interrupt); | 163 | macii_interrupt); |
163 | if (err) goto out; | 164 | if (err) goto out; |
164 | 165 | ||
diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c index 34d02a91b29..9ab5b0c34f0 100644 --- a/drivers/macintosh/via-maciisi.c +++ b/drivers/macintosh/via-maciisi.c | |||
@@ -122,8 +122,8 @@ maciisi_init(void) | |||
122 | return err; | 122 | return err; |
123 | } | 123 | } |
124 | 124 | ||
125 | if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, 0, "ADB", | 125 | if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, IRQ_FLG_LOCK | IRQ_FLG_FAST, |
126 | maciisi_interrupt)) { | 126 | "ADB", maciisi_interrupt)) { |
127 | printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB); | 127 | printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB); |
128 | return -EAGAIN; | 128 | return -EAGAIN; |
129 | } | 129 | } |
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 22b8ce4191c..6cccd60c594 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c | |||
@@ -50,6 +50,7 @@ | |||
50 | #include <asm/machdep.h> | 50 | #include <asm/machdep.h> |
51 | #include <asm/io.h> | 51 | #include <asm/io.h> |
52 | #include <asm/pgtable.h> | 52 | #include <asm/pgtable.h> |
53 | #include <asm/system.h> | ||
53 | #include <asm/sections.h> | 54 | #include <asm/sections.h> |
54 | #include <asm/irq.h> | 55 | #include <asm/irq.h> |
55 | #include <asm/pmac_feature.h> | 56 | #include <asm/pmac_feature.h> |
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c index a00ee41f057..aeb30d07d5a 100644 --- a/drivers/macintosh/via-pmu68k.c +++ b/drivers/macintosh/via-pmu68k.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <asm/mac_via.h> | 37 | #include <asm/mac_via.h> |
38 | 38 | ||
39 | #include <asm/pgtable.h> | 39 | #include <asm/pgtable.h> |
40 | #include <asm/system.h> | ||
40 | #include <asm/irq.h> | 41 | #include <asm/irq.h> |
41 | #include <asm/uaccess.h> | 42 | #include <asm/uaccess.h> |
42 | 43 | ||
diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h index 028cdac2d33..7a2482cc26a 100644 --- a/drivers/macintosh/windfarm.h +++ b/drivers/macintosh/windfarm.h | |||
@@ -17,7 +17,7 @@ | |||
17 | #include <linux/device.h> | 17 | #include <linux/device.h> |
18 | 18 | ||
19 | /* Display a 16.16 fixed point value */ | 19 | /* Display a 16.16 fixed point value */ |
20 | #define FIX32TOPRINT(f) (((s32)(f)) >> 16),(((((s32)(f)) & 0xffff) * 1000) >> 16) | 20 | #define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16) |
21 | 21 | ||
22 | /* | 22 | /* |
23 | * Control objects | 23 | * Control objects |
@@ -35,13 +35,12 @@ struct wf_control_ops { | |||
35 | }; | 35 | }; |
36 | 36 | ||
37 | struct wf_control { | 37 | struct wf_control { |
38 | struct list_head link; | 38 | struct list_head link; |
39 | const struct wf_control_ops *ops; | 39 | struct wf_control_ops *ops; |
40 | const char *name; | 40 | char *name; |
41 | int type; | 41 | int type; |
42 | struct kref ref; | 42 | struct kref ref; |
43 | struct device_attribute attr; | 43 | struct device_attribute attr; |
44 | void *priv; | ||
45 | }; | 44 | }; |
46 | 45 | ||
47 | #define WF_CONTROL_TYPE_GENERIC 0 | 46 | #define WF_CONTROL_TYPE_GENERIC 0 |
@@ -73,26 +72,6 @@ static inline int wf_control_set_min(struct wf_control *ct) | |||
73 | return ct->ops->set_value(ct, vmin); | 72 | return ct->ops->set_value(ct, vmin); |
74 | } | 73 | } |
75 | 74 | ||
76 | static inline int wf_control_set(struct wf_control *ct, s32 val) | ||
77 | { | ||
78 | return ct->ops->set_value(ct, val); | ||
79 | } | ||
80 | |||
81 | static inline int wf_control_get(struct wf_control *ct, s32 *val) | ||
82 | { | ||
83 | return ct->ops->get_value(ct, val); | ||
84 | } | ||
85 | |||
86 | static inline s32 wf_control_get_min(struct wf_control *ct) | ||
87 | { | ||
88 | return ct->ops->get_min(ct); | ||
89 | } | ||
90 | |||
91 | static inline s32 wf_control_get_max(struct wf_control *ct) | ||
92 | { | ||
93 | return ct->ops->get_max(ct); | ||
94 | } | ||
95 | |||
96 | /* | 75 | /* |
97 | * Sensor objects | 76 | * Sensor objects |
98 | */ | 77 | */ |
@@ -106,12 +85,11 @@ struct wf_sensor_ops { | |||
106 | }; | 85 | }; |
107 | 86 | ||
108 | struct wf_sensor { | 87 | struct wf_sensor { |
109 | struct list_head link; | 88 | struct list_head link; |
110 | const struct wf_sensor_ops *ops; | 89 | struct wf_sensor_ops *ops; |
111 | const char *name; | 90 | char *name; |
112 | struct kref ref; | 91 | struct kref ref; |
113 | struct device_attribute attr; | 92 | struct device_attribute attr; |
114 | void *priv; | ||
115 | }; | 93 | }; |
116 | 94 | ||
117 | /* Same lifetime rules as controls */ | 95 | /* Same lifetime rules as controls */ |
@@ -121,11 +99,6 @@ extern struct wf_sensor * wf_find_sensor(const char *name); | |||
121 | extern int wf_get_sensor(struct wf_sensor *sr); | 99 | extern int wf_get_sensor(struct wf_sensor *sr); |
122 | extern void wf_put_sensor(struct wf_sensor *sr); | 100 | extern void wf_put_sensor(struct wf_sensor *sr); |
123 | 101 | ||
124 | static inline int wf_sensor_get(struct wf_sensor *sr, s32 *val) | ||
125 | { | ||
126 | return sr->ops->get_value(sr, val); | ||
127 | } | ||
128 | |||
129 | /* For use by clients. Note that we are a bit racy here since | 102 | /* For use by clients. Note that we are a bit racy here since |
130 | * notifier_block doesn't have a module owner field. I may fix | 103 | * notifier_block doesn't have a module owner field. I may fix |
131 | * it one day ... | 104 | * it one day ... |
diff --git a/drivers/macintosh/windfarm_ad7417_sensor.c b/drivers/macintosh/windfarm_ad7417_sensor.c deleted file mode 100644 index 7c28b71246c..00000000000 --- a/drivers/macintosh/windfarm_ad7417_sensor.c +++ /dev/null | |||
@@ -1,347 +0,0 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. AD7417 sensors | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/wait.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <asm/prom.h> | ||
18 | #include <asm/machdep.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/sections.h> | ||
21 | |||
22 | #include "windfarm.h" | ||
23 | #include "windfarm_mpu.h" | ||
24 | |||
25 | #define VERSION "1.0" | ||
26 | |||
27 | struct wf_ad7417_priv { | ||
28 | struct kref ref; | ||
29 | struct i2c_client *i2c; | ||
30 | u8 config; | ||
31 | u8 cpu; | ||
32 | const struct mpu_data *mpu; | ||
33 | struct wf_sensor sensors[5]; | ||
34 | struct mutex lock; | ||
35 | }; | ||
36 | |||
37 | static int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value) | ||
38 | { | ||
39 | struct wf_ad7417_priv *pv = sr->priv; | ||
40 | u8 buf[2]; | ||
41 | s16 raw; | ||
42 | int rc; | ||
43 | |||
44 | *value = 0; | ||
45 | mutex_lock(&pv->lock); | ||
46 | |||
47 | /* Read temp register */ | ||
48 | buf[0] = 0; | ||
49 | rc = i2c_master_send(pv->i2c, buf, 1); | ||
50 | if (rc < 0) | ||
51 | goto error; | ||
52 | rc = i2c_master_recv(pv->i2c, buf, 2); | ||
53 | if (rc < 0) | ||
54 | goto error; | ||
55 | |||
56 | /* Read a a 16-bit signed value */ | ||
57 | raw = be16_to_cpup((__le16 *)buf); | ||
58 | |||
59 | /* Convert 8.8-bit to 16.16 fixed point */ | ||
60 | *value = ((s32)raw) << 8; | ||
61 | |||
62 | mutex_unlock(&pv->lock); | ||
63 | return 0; | ||
64 | |||
65 | error: | ||
66 | mutex_unlock(&pv->lock); | ||
67 | return -1; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Scaling factors for the AD7417 ADC converters (except | ||
72 | * for the CPU diode which is obtained from the EEPROM). | ||
73 | * Those values are obtained from the property list of | ||
74 | * the darwin driver | ||
75 | */ | ||
76 | #define ADC_12V_CURRENT_SCALE 0x0320 /* _AD2 */ | ||
77 | #define ADC_CPU_VOLTAGE_SCALE 0x00a0 /* _AD3 */ | ||
78 | #define ADC_CPU_CURRENT_SCALE 0x1f40 /* _AD4 */ | ||
79 | |||
80 | static void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv, | ||
81 | int chan, s32 raw, s32 *value) | ||
82 | { | ||
83 | switch(chan) { | ||
84 | case 1: /* Diode */ | ||
85 | *value = (raw * (s32)pv->mpu->mdiode + | ||
86 | ((s32)pv->mpu->bdiode << 12)) >> 2; | ||
87 | break; | ||
88 | case 2: /* 12v current */ | ||
89 | *value = raw * ADC_12V_CURRENT_SCALE; | ||
90 | break; | ||
91 | case 3: /* core voltage */ | ||
92 | *value = raw * ADC_CPU_VOLTAGE_SCALE; | ||
93 | break; | ||
94 | case 4: /* core current */ | ||
95 | *value = raw * ADC_CPU_CURRENT_SCALE; | ||
96 | break; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | static int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value) | ||
101 | { | ||
102 | struct wf_ad7417_priv *pv = sr->priv; | ||
103 | int chan = sr - pv->sensors; | ||
104 | int i, rc; | ||
105 | u8 buf[2]; | ||
106 | u16 raw; | ||
107 | |||
108 | *value = 0; | ||
109 | mutex_lock(&pv->lock); | ||
110 | for (i = 0; i < 10; i++) { | ||
111 | /* Set channel */ | ||
112 | buf[0] = 1; | ||
113 | buf[1] = (pv->config & 0x1f) | (chan << 5); | ||
114 | rc = i2c_master_send(pv->i2c, buf, 2); | ||
115 | if (rc < 0) | ||
116 | goto error; | ||
117 | |||
118 | /* Wait for conversion */ | ||
119 | msleep(1); | ||
120 | |||
121 | /* Switch to data register */ | ||
122 | buf[0] = 4; | ||
123 | rc = i2c_master_send(pv->i2c, buf, 1); | ||
124 | if (rc < 0) | ||
125 | goto error; | ||
126 | |||
127 | /* Read result */ | ||
128 | rc = i2c_master_recv(pv->i2c, buf, 2); | ||
129 | if (rc < 0) | ||
130 | goto error; | ||
131 | |||
132 | /* Read a a 16-bit signed value */ | ||
133 | raw = be16_to_cpup((__le16 *)buf) >> 6; | ||
134 | wf_ad7417_adc_convert(pv, chan, raw, value); | ||
135 | |||
136 | dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]" | ||
137 | " raw value: 0x%x, conv to: 0x%08x\n", | ||
138 | chan, sr->name, raw, *value); | ||
139 | |||
140 | mutex_unlock(&pv->lock); | ||
141 | return 0; | ||
142 | |||
143 | error: | ||
144 | dev_dbg(&pv->i2c->dev, | ||
145 | "Error reading ADC, try %d...\n", i); | ||
146 | if (i < 9) | ||
147 | msleep(10); | ||
148 | } | ||
149 | mutex_unlock(&pv->lock); | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | static void wf_ad7417_release(struct kref *ref) | ||
154 | { | ||
155 | struct wf_ad7417_priv *pv = container_of(ref, | ||
156 | struct wf_ad7417_priv, ref); | ||
157 | kfree(pv); | ||
158 | } | ||
159 | |||
160 | static void wf_ad7417_sensor_release(struct wf_sensor *sr) | ||
161 | { | ||
162 | struct wf_ad7417_priv *pv = sr->priv; | ||
163 | |||
164 | kfree(sr->name); | ||
165 | kref_put(&pv->ref, wf_ad7417_release); | ||
166 | } | ||
167 | |||
168 | static const struct wf_sensor_ops wf_ad7417_temp_ops = { | ||
169 | .get_value = wf_ad7417_temp_get, | ||
170 | .release = wf_ad7417_sensor_release, | ||
171 | .owner = THIS_MODULE, | ||
172 | }; | ||
173 | |||
174 | static const struct wf_sensor_ops wf_ad7417_adc_ops = { | ||
175 | .get_value = wf_ad7417_adc_get, | ||
176 | .release = wf_ad7417_sensor_release, | ||
177 | .owner = THIS_MODULE, | ||
178 | }; | ||
179 | |||
180 | static void wf_ad7417_add_sensor(struct wf_ad7417_priv *pv, | ||
181 | int index, const char *name, | ||
182 | const struct wf_sensor_ops *ops) | ||
183 | { | ||
184 | pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu); | ||
185 | pv->sensors[index].priv = pv; | ||
186 | pv->sensors[index].ops = ops; | ||
187 | if (!wf_register_sensor(&pv->sensors[index])) | ||
188 | kref_get(&pv->ref); | ||
189 | } | ||
190 | |||
191 | static void wf_ad7417_init_chip(struct wf_ad7417_priv *pv) | ||
192 | { | ||
193 | int rc; | ||
194 | u8 buf[2]; | ||
195 | u8 config = 0; | ||
196 | |||
197 | /* | ||
198 | * Read ADC the configuration register and cache it. We | ||
199 | * also make sure Config2 contains proper values, I've seen | ||
200 | * cases where we got stale grabage in there, thus preventing | ||
201 | * proper reading of conv. values | ||
202 | */ | ||
203 | |||
204 | /* Clear Config2 */ | ||
205 | buf[0] = 5; | ||
206 | buf[1] = 0; | ||
207 | i2c_master_send(pv->i2c, buf, 2); | ||
208 | |||
209 | /* Read & cache Config1 */ | ||
210 | buf[0] = 1; | ||
211 | rc = i2c_master_send(pv->i2c, buf, 1); | ||
212 | if (rc > 0) { | ||
213 | rc = i2c_master_recv(pv->i2c, buf, 1); | ||
214 | if (rc > 0) { | ||
215 | config = buf[0]; | ||
216 | |||
217 | dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n", | ||
218 | config); | ||
219 | |||
220 | /* Disable shutdown mode */ | ||
221 | config &= 0xfe; | ||
222 | buf[0] = 1; | ||
223 | buf[1] = config; | ||
224 | rc = i2c_master_send(pv->i2c, buf, 2); | ||
225 | } | ||
226 | } | ||
227 | if (rc <= 0) | ||
228 | dev_err(&pv->i2c->dev, "Error reading ADC config\n"); | ||
229 | |||
230 | pv->config = config; | ||
231 | } | ||
232 | |||
233 | static int wf_ad7417_probe(struct i2c_client *client, | ||
234 | const struct i2c_device_id *id) | ||
235 | { | ||
236 | struct wf_ad7417_priv *pv; | ||
237 | const struct mpu_data *mpu; | ||
238 | const char *loc; | ||
239 | int cpu_nr; | ||
240 | |||
241 | loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); | ||
242 | if (!loc) { | ||
243 | dev_warn(&client->dev, "Missing hwsensor-location property!\n"); | ||
244 | return -ENXIO; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Identify which CPU we belong to by looking at the first entry | ||
249 | * in the hwsensor-location list | ||
250 | */ | ||
251 | if (!strncmp(loc, "CPU A", 5)) | ||
252 | cpu_nr = 0; | ||
253 | else if (!strncmp(loc, "CPU B", 5)) | ||
254 | cpu_nr = 1; | ||
255 | else { | ||
256 | pr_err("wf_ad7417: Can't identify location %s\n", loc); | ||
257 | return -ENXIO; | ||
258 | } | ||
259 | mpu = wf_get_mpu(cpu_nr); | ||
260 | if (!mpu) { | ||
261 | dev_err(&client->dev, "Failed to retrieve MPU data\n"); | ||
262 | return -ENXIO; | ||
263 | } | ||
264 | |||
265 | pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL); | ||
266 | if (pv == NULL) | ||
267 | return -ENODEV; | ||
268 | |||
269 | kref_init(&pv->ref); | ||
270 | mutex_init(&pv->lock); | ||
271 | pv->i2c = client; | ||
272 | pv->cpu = cpu_nr; | ||
273 | pv->mpu = mpu; | ||
274 | dev_set_drvdata(&client->dev, pv); | ||
275 | |||
276 | /* Initialize the chip */ | ||
277 | wf_ad7417_init_chip(pv); | ||
278 | |||
279 | /* | ||
280 | * We cannot rely on Apple device-tree giving us child | ||
281 | * node with the names of the individual sensors so we | ||
282 | * just hard code what we know about them | ||
283 | */ | ||
284 | wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops); | ||
285 | wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops); | ||
286 | wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops); | ||
287 | wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops); | ||
288 | wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops); | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static int wf_ad7417_remove(struct i2c_client *client) | ||
294 | { | ||
295 | struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev); | ||
296 | int i; | ||
297 | |||
298 | /* Mark client detached */ | ||
299 | pv->i2c = NULL; | ||
300 | |||
301 | /* Release sensor */ | ||
302 | for (i = 0; i < 5; i++) | ||
303 | wf_unregister_sensor(&pv->sensors[i]); | ||
304 | |||
305 | kref_put(&pv->ref, wf_ad7417_release); | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | static const struct i2c_device_id wf_ad7417_id[] = { | ||
311 | { "MAC,ad7417", 0 }, | ||
312 | { } | ||
313 | }; | ||
314 | MODULE_DEVICE_TABLE(i2c, wf_ad7417_id); | ||
315 | |||
316 | static struct i2c_driver wf_ad7417_driver = { | ||
317 | .driver = { | ||
318 | .name = "wf_ad7417", | ||
319 | }, | ||
320 | .probe = wf_ad7417_probe, | ||
321 | .remove = wf_ad7417_remove, | ||
322 | .id_table = wf_ad7417_id, | ||
323 | }; | ||
324 | |||
325 | static int wf_ad7417_init(void) | ||
326 | { | ||
327 | /* This is only supported on these machines */ | ||
328 | if (!of_machine_is_compatible("PowerMac7,2") && | ||
329 | !of_machine_is_compatible("PowerMac7,3") && | ||
330 | !of_machine_is_compatible("RackMac3,1")) | ||
331 | return -ENODEV; | ||
332 | |||
333 | return i2c_add_driver(&wf_ad7417_driver); | ||
334 | } | ||
335 | |||
336 | static void wf_ad7417_exit(void) | ||
337 | { | ||
338 | i2c_del_driver(&wf_ad7417_driver); | ||
339 | } | ||
340 | |||
341 | module_init(wf_ad7417_init); | ||
342 | module_exit(wf_ad7417_exit); | ||
343 | |||
344 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
345 | MODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs"); | ||
346 | MODULE_LICENSE("GPL"); | ||
347 | |||
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 3ee198b6584..ce8897933a8 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c | |||
@@ -164,27 +164,13 @@ static ssize_t wf_show_control(struct device *dev, | |||
164 | struct device_attribute *attr, char *buf) | 164 | struct device_attribute *attr, char *buf) |
165 | { | 165 | { |
166 | struct wf_control *ctrl = container_of(attr, struct wf_control, attr); | 166 | struct wf_control *ctrl = container_of(attr, struct wf_control, attr); |
167 | const char *typestr; | ||
168 | s32 val = 0; | 167 | s32 val = 0; |
169 | int err; | 168 | int err; |
170 | 169 | ||
171 | err = ctrl->ops->get_value(ctrl, &val); | 170 | err = ctrl->ops->get_value(ctrl, &val); |
172 | if (err < 0) { | 171 | if (err < 0) |
173 | if (err == -EFAULT) | ||
174 | return sprintf(buf, "<HW FAULT>\n"); | ||
175 | return err; | 172 | return err; |
176 | } | 173 | return sprintf(buf, "%d\n", val); |
177 | switch(ctrl->type) { | ||
178 | case WF_CONTROL_RPM_FAN: | ||
179 | typestr = " RPM"; | ||
180 | break; | ||
181 | case WF_CONTROL_PWM_FAN: | ||
182 | typestr = " %"; | ||
183 | break; | ||
184 | default: | ||
185 | typestr = ""; | ||
186 | } | ||
187 | return sprintf(buf, "%d%s\n", val, typestr); | ||
188 | } | 174 | } |
189 | 175 | ||
190 | /* This is really only for debugging... */ | 176 | /* This is really only for debugging... */ |
@@ -484,6 +470,11 @@ static int __init windfarm_core_init(void) | |||
484 | { | 470 | { |
485 | DBG("wf: core loaded\n"); | 471 | DBG("wf: core loaded\n"); |
486 | 472 | ||
473 | /* Don't register on old machines that use therm_pm72 for now */ | ||
474 | if (of_machine_is_compatible("PowerMac7,2") || | ||
475 | of_machine_is_compatible("PowerMac7,3") || | ||
476 | of_machine_is_compatible("RackMac3,1")) | ||
477 | return -ENODEV; | ||
487 | platform_device_register(&wf_platform_device); | 478 | platform_device_register(&wf_platform_device); |
488 | return 0; | 479 | return 0; |
489 | } | 480 | } |
diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c index 72d1fdfe02a..1a77a7c97d0 100644 --- a/drivers/macintosh/windfarm_cpufreq_clamp.c +++ b/drivers/macintosh/windfarm_cpufreq_clamp.c | |||
@@ -75,6 +75,12 @@ static int __init wf_cpufreq_clamp_init(void) | |||
75 | { | 75 | { |
76 | struct wf_control *clamp; | 76 | struct wf_control *clamp; |
77 | 77 | ||
78 | /* Don't register on old machines that use therm_pm72 for now */ | ||
79 | if (of_machine_is_compatible("PowerMac7,2") || | ||
80 | of_machine_is_compatible("PowerMac7,3") || | ||
81 | of_machine_is_compatible("RackMac3,1")) | ||
82 | return -ENODEV; | ||
83 | |||
78 | clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); | 84 | clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); |
79 | if (clamp == NULL) | 85 | if (clamp == NULL) |
80 | return -ENOMEM; | 86 | return -ENOMEM; |
diff --git a/drivers/macintosh/windfarm_fcu_controls.c b/drivers/macintosh/windfarm_fcu_controls.c deleted file mode 100644 index 0226b796a21..00000000000 --- a/drivers/macintosh/windfarm_fcu_controls.c +++ /dev/null | |||
@@ -1,600 +0,0 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. FCU fan control | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | */ | ||
8 | #undef DEBUG | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/wait.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <asm/prom.h> | ||
19 | #include <asm/machdep.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/sections.h> | ||
22 | |||
23 | #include "windfarm.h" | ||
24 | #include "windfarm_mpu.h" | ||
25 | |||
26 | #define VERSION "1.0" | ||
27 | |||
28 | #ifdef DEBUG | ||
29 | #define DBG(args...) printk(args) | ||
30 | #else | ||
31 | #define DBG(args...) do { } while(0) | ||
32 | #endif | ||
33 | |||
34 | /* | ||
35 | * This option is "weird" :) Basically, if you define this to 1 | ||
36 | * the control loop for the RPMs fans (not PWMs) will apply the | ||
37 | * correction factor obtained from the PID to the actual RPM | ||
38 | * speed read from the FCU. | ||
39 | * | ||
40 | * If you define the below constant to 0, then it will be | ||
41 | * applied to the setpoint RPM speed, that is basically the | ||
42 | * speed we proviously "asked" for. | ||
43 | * | ||
44 | * I'm using 0 for now which is what therm_pm72 used to do and | ||
45 | * what Darwin -apparently- does based on observed behaviour. | ||
46 | */ | ||
47 | #define RPM_PID_USE_ACTUAL_SPEED 0 | ||
48 | |||
49 | /* Default min/max for pumps */ | ||
50 | #define CPU_PUMP_OUTPUT_MAX 3200 | ||
51 | #define CPU_PUMP_OUTPUT_MIN 1250 | ||
52 | |||
53 | #define FCU_FAN_RPM 0 | ||
54 | #define FCU_FAN_PWM 1 | ||
55 | |||
56 | struct wf_fcu_priv { | ||
57 | struct kref ref; | ||
58 | struct i2c_client *i2c; | ||
59 | struct mutex lock; | ||
60 | struct list_head fan_list; | ||
61 | int rpm_shift; | ||
62 | }; | ||
63 | |||
64 | struct wf_fcu_fan { | ||
65 | struct list_head link; | ||
66 | int id; | ||
67 | s32 min, max, target; | ||
68 | struct wf_fcu_priv *fcu_priv; | ||
69 | struct wf_control ctrl; | ||
70 | }; | ||
71 | |||
72 | static void wf_fcu_release(struct kref *ref) | ||
73 | { | ||
74 | struct wf_fcu_priv *pv = container_of(ref, struct wf_fcu_priv, ref); | ||
75 | |||
76 | kfree(pv); | ||
77 | } | ||
78 | |||
79 | static void wf_fcu_fan_release(struct wf_control *ct) | ||
80 | { | ||
81 | struct wf_fcu_fan *fan = ct->priv; | ||
82 | |||
83 | kref_put(&fan->fcu_priv->ref, wf_fcu_release); | ||
84 | kfree(fan); | ||
85 | } | ||
86 | |||
87 | static int wf_fcu_read_reg(struct wf_fcu_priv *pv, int reg, | ||
88 | unsigned char *buf, int nb) | ||
89 | { | ||
90 | int tries, nr, nw; | ||
91 | |||
92 | mutex_lock(&pv->lock); | ||
93 | |||
94 | buf[0] = reg; | ||
95 | tries = 0; | ||
96 | for (;;) { | ||
97 | nw = i2c_master_send(pv->i2c, buf, 1); | ||
98 | if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) | ||
99 | break; | ||
100 | msleep(10); | ||
101 | ++tries; | ||
102 | } | ||
103 | if (nw <= 0) { | ||
104 | pr_err("Failure writing address to FCU: %d", nw); | ||
105 | nr = nw; | ||
106 | goto bail; | ||
107 | } | ||
108 | tries = 0; | ||
109 | for (;;) { | ||
110 | nr = i2c_master_recv(pv->i2c, buf, nb); | ||
111 | if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100) | ||
112 | break; | ||
113 | msleep(10); | ||
114 | ++tries; | ||
115 | } | ||
116 | if (nr <= 0) | ||
117 | pr_err("wf_fcu: Failure reading data from FCU: %d", nw); | ||
118 | bail: | ||
119 | mutex_unlock(&pv->lock); | ||
120 | return nr; | ||
121 | } | ||
122 | |||
123 | static int wf_fcu_write_reg(struct wf_fcu_priv *pv, int reg, | ||
124 | const unsigned char *ptr, int nb) | ||
125 | { | ||
126 | int tries, nw; | ||
127 | unsigned char buf[16]; | ||
128 | |||
129 | buf[0] = reg; | ||
130 | memcpy(buf+1, ptr, nb); | ||
131 | ++nb; | ||
132 | tries = 0; | ||
133 | for (;;) { | ||
134 | nw = i2c_master_send(pv->i2c, buf, nb); | ||
135 | if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) | ||
136 | break; | ||
137 | msleep(10); | ||
138 | ++tries; | ||
139 | } | ||
140 | if (nw < 0) | ||
141 | pr_err("wf_fcu: Failure writing to FCU: %d", nw); | ||
142 | return nw; | ||
143 | } | ||
144 | |||
145 | static int wf_fcu_fan_set_rpm(struct wf_control *ct, s32 value) | ||
146 | { | ||
147 | struct wf_fcu_fan *fan = ct->priv; | ||
148 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
149 | int rc, shift = pv->rpm_shift; | ||
150 | unsigned char buf[2]; | ||
151 | |||
152 | if (value < fan->min) | ||
153 | value = fan->min; | ||
154 | if (value > fan->max) | ||
155 | value = fan->max; | ||
156 | |||
157 | fan->target = value; | ||
158 | |||
159 | buf[0] = value >> (8 - shift); | ||
160 | buf[1] = value << shift; | ||
161 | rc = wf_fcu_write_reg(pv, 0x10 + (fan->id * 2), buf, 2); | ||
162 | if (rc < 0) | ||
163 | return -EIO; | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int wf_fcu_fan_get_rpm(struct wf_control *ct, s32 *value) | ||
168 | { | ||
169 | struct wf_fcu_fan *fan = ct->priv; | ||
170 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
171 | int rc, reg_base, shift = pv->rpm_shift; | ||
172 | unsigned char failure; | ||
173 | unsigned char active; | ||
174 | unsigned char buf[2]; | ||
175 | |||
176 | rc = wf_fcu_read_reg(pv, 0xb, &failure, 1); | ||
177 | if (rc != 1) | ||
178 | return -EIO; | ||
179 | if ((failure & (1 << fan->id)) != 0) | ||
180 | return -EFAULT; | ||
181 | rc = wf_fcu_read_reg(pv, 0xd, &active, 1); | ||
182 | if (rc != 1) | ||
183 | return -EIO; | ||
184 | if ((active & (1 << fan->id)) == 0) | ||
185 | return -ENXIO; | ||
186 | |||
187 | /* Programmed value or real current speed */ | ||
188 | #if RPM_PID_USE_ACTUAL_SPEED | ||
189 | reg_base = 0x11; | ||
190 | #else | ||
191 | reg_base = 0x10; | ||
192 | #endif | ||
193 | rc = wf_fcu_read_reg(pv, reg_base + (fan->id * 2), buf, 2); | ||
194 | if (rc != 2) | ||
195 | return -EIO; | ||
196 | |||
197 | *value = (buf[0] << (8 - shift)) | buf[1] >> shift; | ||
198 | |||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int wf_fcu_fan_set_pwm(struct wf_control *ct, s32 value) | ||
203 | { | ||
204 | struct wf_fcu_fan *fan = ct->priv; | ||
205 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
206 | unsigned char buf[2]; | ||
207 | int rc; | ||
208 | |||
209 | if (value < fan->min) | ||
210 | value = fan->min; | ||
211 | if (value > fan->max) | ||
212 | value = fan->max; | ||
213 | |||
214 | fan->target = value; | ||
215 | |||
216 | value = (value * 2559) / 1000; | ||
217 | buf[0] = value; | ||
218 | rc = wf_fcu_write_reg(pv, 0x30 + (fan->id * 2), buf, 1); | ||
219 | if (rc < 0) | ||
220 | return -EIO; | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int wf_fcu_fan_get_pwm(struct wf_control *ct, s32 *value) | ||
225 | { | ||
226 | struct wf_fcu_fan *fan = ct->priv; | ||
227 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
228 | unsigned char failure; | ||
229 | unsigned char active; | ||
230 | unsigned char buf[2]; | ||
231 | int rc; | ||
232 | |||
233 | rc = wf_fcu_read_reg(pv, 0x2b, &failure, 1); | ||
234 | if (rc != 1) | ||
235 | return -EIO; | ||
236 | if ((failure & (1 << fan->id)) != 0) | ||
237 | return -EFAULT; | ||
238 | rc = wf_fcu_read_reg(pv, 0x2d, &active, 1); | ||
239 | if (rc != 1) | ||
240 | return -EIO; | ||
241 | if ((active & (1 << fan->id)) == 0) | ||
242 | return -ENXIO; | ||
243 | |||
244 | rc = wf_fcu_read_reg(pv, 0x30 + (fan->id * 2), buf, 1); | ||
245 | if (rc != 1) | ||
246 | return -EIO; | ||
247 | |||
248 | *value = (((s32)buf[0]) * 1000) / 2559; | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static s32 wf_fcu_fan_min(struct wf_control *ct) | ||
254 | { | ||
255 | struct wf_fcu_fan *fan = ct->priv; | ||
256 | |||
257 | return fan->min; | ||
258 | } | ||
259 | |||
260 | static s32 wf_fcu_fan_max(struct wf_control *ct) | ||
261 | { | ||
262 | struct wf_fcu_fan *fan = ct->priv; | ||
263 | |||
264 | return fan->max; | ||
265 | } | ||
266 | |||
267 | static const struct wf_control_ops wf_fcu_fan_rpm_ops = { | ||
268 | .set_value = wf_fcu_fan_set_rpm, | ||
269 | .get_value = wf_fcu_fan_get_rpm, | ||
270 | .get_min = wf_fcu_fan_min, | ||
271 | .get_max = wf_fcu_fan_max, | ||
272 | .release = wf_fcu_fan_release, | ||
273 | .owner = THIS_MODULE, | ||
274 | }; | ||
275 | |||
276 | static const struct wf_control_ops wf_fcu_fan_pwm_ops = { | ||
277 | .set_value = wf_fcu_fan_set_pwm, | ||
278 | .get_value = wf_fcu_fan_get_pwm, | ||
279 | .get_min = wf_fcu_fan_min, | ||
280 | .get_max = wf_fcu_fan_max, | ||
281 | .release = wf_fcu_fan_release, | ||
282 | .owner = THIS_MODULE, | ||
283 | }; | ||
284 | |||
285 | static void wf_fcu_get_pump_minmax(struct wf_fcu_fan *fan) | ||
286 | { | ||
287 | const struct mpu_data *mpu = wf_get_mpu(0); | ||
288 | u16 pump_min = 0, pump_max = 0xffff; | ||
289 | u16 tmp[4]; | ||
290 | |||
291 | /* Try to fetch pumps min/max infos from eeprom */ | ||
292 | if (mpu) { | ||
293 | memcpy(&tmp, mpu->processor_part_num, 8); | ||
294 | if (tmp[0] != 0xffff && tmp[1] != 0xffff) { | ||
295 | pump_min = max(pump_min, tmp[0]); | ||
296 | pump_max = min(pump_max, tmp[1]); | ||
297 | } | ||
298 | if (tmp[2] != 0xffff && tmp[3] != 0xffff) { | ||
299 | pump_min = max(pump_min, tmp[2]); | ||
300 | pump_max = min(pump_max, tmp[3]); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | /* Double check the values, this _IS_ needed as the EEPROM on | ||
305 | * some dual 2.5Ghz G5s seem, at least, to have both min & max | ||
306 | * same to the same value ... (grrrr) | ||
307 | */ | ||
308 | if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) { | ||
309 | pump_min = CPU_PUMP_OUTPUT_MIN; | ||
310 | pump_max = CPU_PUMP_OUTPUT_MAX; | ||
311 | } | ||
312 | |||
313 | fan->min = pump_min; | ||
314 | fan->max = pump_max; | ||
315 | |||
316 | DBG("wf_fcu: pump min/max for %s set to: [%d..%d] RPM\n", | ||
317 | fan->ctrl.name, pump_min, pump_max); | ||
318 | } | ||
319 | |||
320 | static void wf_fcu_get_rpmfan_minmax(struct wf_fcu_fan *fan) | ||
321 | { | ||
322 | struct wf_fcu_priv *pv = fan->fcu_priv; | ||
323 | const struct mpu_data *mpu0 = wf_get_mpu(0); | ||
324 | const struct mpu_data *mpu1 = wf_get_mpu(1); | ||
325 | |||
326 | /* Default */ | ||
327 | fan->min = 2400 >> pv->rpm_shift; | ||
328 | fan->max = 56000 >> pv->rpm_shift; | ||
329 | |||
330 | /* CPU fans have min/max in MPU */ | ||
331 | if (mpu0 && !strcmp(fan->ctrl.name, "cpu-front-fan-0")) { | ||
332 | fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); | ||
333 | fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); | ||
334 | goto bail; | ||
335 | } | ||
336 | if (mpu1 && !strcmp(fan->ctrl.name, "cpu-front-fan-1")) { | ||
337 | fan->min = max(fan->min, (s32)mpu1->rminn_intake_fan); | ||
338 | fan->max = min(fan->max, (s32)mpu1->rmaxn_intake_fan); | ||
339 | goto bail; | ||
340 | } | ||
341 | if (mpu0 && !strcmp(fan->ctrl.name, "cpu-rear-fan-0")) { | ||
342 | fan->min = max(fan->min, (s32)mpu0->rminn_exhaust_fan); | ||
343 | fan->max = min(fan->max, (s32)mpu0->rmaxn_exhaust_fan); | ||
344 | goto bail; | ||
345 | } | ||
346 | if (mpu1 && !strcmp(fan->ctrl.name, "cpu-rear-fan-1")) { | ||
347 | fan->min = max(fan->min, (s32)mpu1->rminn_exhaust_fan); | ||
348 | fan->max = min(fan->max, (s32)mpu1->rmaxn_exhaust_fan); | ||
349 | goto bail; | ||
350 | } | ||
351 | /* Rackmac variants, we just use mpu0 intake */ | ||
352 | if (!strncmp(fan->ctrl.name, "cpu-fan", 7)) { | ||
353 | fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); | ||
354 | fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); | ||
355 | goto bail; | ||
356 | } | ||
357 | bail: | ||
358 | DBG("wf_fcu: fan min/max for %s set to: [%d..%d] RPM\n", | ||
359 | fan->ctrl.name, fan->min, fan->max); | ||
360 | } | ||
361 | |||
362 | static void wf_fcu_add_fan(struct wf_fcu_priv *pv, const char *name, | ||
363 | int type, int id) | ||
364 | { | ||
365 | struct wf_fcu_fan *fan; | ||
366 | |||
367 | fan = kzalloc(sizeof(*fan), GFP_KERNEL); | ||
368 | if (!fan) | ||
369 | return; | ||
370 | fan->fcu_priv = pv; | ||
371 | fan->id = id; | ||
372 | fan->ctrl.name = name; | ||
373 | fan->ctrl.priv = fan; | ||
374 | |||
375 | /* min/max is oddball but the code comes from | ||
376 | * therm_pm72 which seems to work so ... | ||
377 | */ | ||
378 | if (type == FCU_FAN_RPM) { | ||
379 | if (!strncmp(name, "cpu-pump", strlen("cpu-pump"))) | ||
380 | wf_fcu_get_pump_minmax(fan); | ||
381 | else | ||
382 | wf_fcu_get_rpmfan_minmax(fan); | ||
383 | fan->ctrl.type = WF_CONTROL_RPM_FAN; | ||
384 | fan->ctrl.ops = &wf_fcu_fan_rpm_ops; | ||
385 | } else { | ||
386 | fan->min = 10; | ||
387 | fan->max = 100; | ||
388 | fan->ctrl.type = WF_CONTROL_PWM_FAN; | ||
389 | fan->ctrl.ops = &wf_fcu_fan_pwm_ops; | ||
390 | } | ||
391 | |||
392 | if (wf_register_control(&fan->ctrl)) { | ||
393 | pr_err("wf_fcu: Failed to register fan %s\n", name); | ||
394 | kfree(fan); | ||
395 | return; | ||
396 | } | ||
397 | list_add(&fan->link, &pv->fan_list); | ||
398 | kref_get(&pv->ref); | ||
399 | } | ||
400 | |||
401 | static void wf_fcu_lookup_fans(struct wf_fcu_priv *pv) | ||
402 | { | ||
403 | /* Translation of device-tree location properties to | ||
404 | * windfarm fan names | ||
405 | */ | ||
406 | static const struct { | ||
407 | const char *dt_name; /* Device-tree name */ | ||
408 | const char *ct_name; /* Control name */ | ||
409 | } loc_trans[] = { | ||
410 | { "BACKSIDE", "backside-fan", }, | ||
411 | { "SYS CTRLR FAN", "backside-fan", }, | ||
412 | { "DRIVE BAY", "drive-bay-fan", }, | ||
413 | { "SLOT", "slots-fan", }, | ||
414 | { "PCI FAN", "slots-fan", }, | ||
415 | { "CPU A INTAKE", "cpu-front-fan-0", }, | ||
416 | { "CPU A EXHAUST", "cpu-rear-fan-0", }, | ||
417 | { "CPU B INTAKE", "cpu-front-fan-1", }, | ||
418 | { "CPU B EXHAUST", "cpu-rear-fan-1", }, | ||
419 | { "CPU A PUMP", "cpu-pump-0", }, | ||
420 | { "CPU B PUMP", "cpu-pump-1", }, | ||
421 | { "CPU A 1", "cpu-fan-a-0", }, | ||
422 | { "CPU A 2", "cpu-fan-b-0", }, | ||
423 | { "CPU A 3", "cpu-fan-c-0", }, | ||
424 | { "CPU B 1", "cpu-fan-a-1", }, | ||
425 | { "CPU B 2", "cpu-fan-b-1", }, | ||
426 | { "CPU B 3", "cpu-fan-c-1", }, | ||
427 | }; | ||
428 | struct device_node *np = NULL, *fcu = pv->i2c->dev.of_node; | ||
429 | int i; | ||
430 | |||
431 | DBG("Looking up FCU controls in device-tree...\n"); | ||
432 | |||
433 | while ((np = of_get_next_child(fcu, np)) != NULL) { | ||
434 | int id, type = -1; | ||
435 | const char *loc; | ||
436 | const char *name; | ||
437 | const u32 *reg; | ||
438 | |||
439 | DBG(" control: %s, type: %s\n", np->name, np->type); | ||
440 | |||
441 | /* Detect control type */ | ||
442 | if (!strcmp(np->type, "fan-rpm-control") || | ||
443 | !strcmp(np->type, "fan-rpm")) | ||
444 | type = FCU_FAN_RPM; | ||
445 | if (!strcmp(np->type, "fan-pwm-control") || | ||
446 | !strcmp(np->type, "fan-pwm")) | ||
447 | type = FCU_FAN_PWM; | ||
448 | /* Only care about fans for now */ | ||
449 | if (type == -1) | ||
450 | continue; | ||
451 | |||
452 | /* Lookup for a matching location */ | ||
453 | loc = of_get_property(np, "location", NULL); | ||
454 | reg = of_get_property(np, "reg", NULL); | ||
455 | if (loc == NULL || reg == NULL) | ||
456 | continue; | ||
457 | DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); | ||
458 | |||
459 | for (i = 0; i < ARRAY_SIZE(loc_trans); i++) { | ||
460 | if (strncmp(loc, loc_trans[i].dt_name, | ||
461 | strlen(loc_trans[i].dt_name))) | ||
462 | continue; | ||
463 | name = loc_trans[i].ct_name; | ||
464 | |||
465 | DBG(" location match, name: %s\n", name); | ||
466 | |||
467 | if (type == FCU_FAN_RPM) | ||
468 | id = ((*reg) - 0x10) / 2; | ||
469 | else | ||
470 | id = ((*reg) - 0x30) / 2; | ||
471 | if (id > 7) { | ||
472 | pr_warning("wf_fcu: Can't parse " | ||
473 | "fan ID in device-tree for %s\n", | ||
474 | np->full_name); | ||
475 | break; | ||
476 | } | ||
477 | wf_fcu_add_fan(pv, name, type, id); | ||
478 | break; | ||
479 | } | ||
480 | } | ||
481 | } | ||
482 | |||
483 | static void wf_fcu_default_fans(struct wf_fcu_priv *pv) | ||
484 | { | ||
485 | /* We only support the default fans for PowerMac7,2 */ | ||
486 | if (!of_machine_is_compatible("PowerMac7,2")) | ||
487 | return; | ||
488 | |||
489 | wf_fcu_add_fan(pv, "backside-fan", FCU_FAN_PWM, 1); | ||
490 | wf_fcu_add_fan(pv, "drive-bay-fan", FCU_FAN_RPM, 2); | ||
491 | wf_fcu_add_fan(pv, "slots-fan", FCU_FAN_PWM, 2); | ||
492 | wf_fcu_add_fan(pv, "cpu-front-fan-0", FCU_FAN_RPM, 3); | ||
493 | wf_fcu_add_fan(pv, "cpu-rear-fan-0", FCU_FAN_RPM, 4); | ||
494 | wf_fcu_add_fan(pv, "cpu-front-fan-1", FCU_FAN_RPM, 5); | ||
495 | wf_fcu_add_fan(pv, "cpu-rear-fan-1", FCU_FAN_RPM, 6); | ||
496 | } | ||
497 | |||
498 | static int wf_fcu_init_chip(struct wf_fcu_priv *pv) | ||
499 | { | ||
500 | unsigned char buf = 0xff; | ||
501 | int rc; | ||
502 | |||
503 | rc = wf_fcu_write_reg(pv, 0xe, &buf, 1); | ||
504 | if (rc < 0) | ||
505 | return -EIO; | ||
506 | rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1); | ||
507 | if (rc < 0) | ||
508 | return -EIO; | ||
509 | rc = wf_fcu_read_reg(pv, 0, &buf, 1); | ||
510 | if (rc < 0) | ||
511 | return -EIO; | ||
512 | pv->rpm_shift = (buf == 1) ? 2 : 3; | ||
513 | |||
514 | pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n", | ||
515 | pv->rpm_shift); | ||
516 | |||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static int wf_fcu_probe(struct i2c_client *client, | ||
521 | const struct i2c_device_id *id) | ||
522 | { | ||
523 | struct wf_fcu_priv *pv; | ||
524 | |||
525 | pv = kzalloc(sizeof(*pv), GFP_KERNEL); | ||
526 | if (!pv) | ||
527 | return -ENOMEM; | ||
528 | |||
529 | kref_init(&pv->ref); | ||
530 | mutex_init(&pv->lock); | ||
531 | INIT_LIST_HEAD(&pv->fan_list); | ||
532 | pv->i2c = client; | ||
533 | |||
534 | /* | ||
535 | * First we must start the FCU which will query the | ||
536 | * shift value to apply to RPMs | ||
537 | */ | ||
538 | if (wf_fcu_init_chip(pv)) { | ||
539 | pr_err("wf_fcu: Initialization failed !\n"); | ||
540 | kfree(pv); | ||
541 | return -ENXIO; | ||
542 | } | ||
543 | |||
544 | /* First lookup fans in the device-tree */ | ||
545 | wf_fcu_lookup_fans(pv); | ||
546 | |||
547 | /* | ||
548 | * Older machines don't have the device-tree entries | ||
549 | * we are looking for, just hard code the list | ||
550 | */ | ||
551 | if (list_empty(&pv->fan_list)) | ||
552 | wf_fcu_default_fans(pv); | ||
553 | |||
554 | /* Still no fans ? FAIL */ | ||
555 | if (list_empty(&pv->fan_list)) { | ||
556 | pr_err("wf_fcu: Failed to find fans for your machine\n"); | ||
557 | kfree(pv); | ||
558 | return -ENODEV; | ||
559 | } | ||
560 | |||
561 | dev_set_drvdata(&client->dev, pv); | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | static int wf_fcu_remove(struct i2c_client *client) | ||
567 | { | ||
568 | struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev); | ||
569 | struct wf_fcu_fan *fan; | ||
570 | |||
571 | while (!list_empty(&pv->fan_list)) { | ||
572 | fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link); | ||
573 | list_del(&fan->link); | ||
574 | wf_unregister_control(&fan->ctrl); | ||
575 | } | ||
576 | kref_put(&pv->ref, wf_fcu_release); | ||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static const struct i2c_device_id wf_fcu_id[] = { | ||
581 | { "MAC,fcu", 0 }, | ||
582 | { } | ||
583 | }; | ||
584 | MODULE_DEVICE_TABLE(i2c, wf_fcu_id); | ||
585 | |||
586 | static struct i2c_driver wf_fcu_driver = { | ||
587 | .driver = { | ||
588 | .name = "wf_fcu", | ||
589 | }, | ||
590 | .probe = wf_fcu_probe, | ||
591 | .remove = wf_fcu_remove, | ||
592 | .id_table = wf_fcu_id, | ||
593 | }; | ||
594 | |||
595 | module_i2c_driver(wf_fcu_driver); | ||
596 | |||
597 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
598 | MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control"); | ||
599 | MODULE_LICENSE("GPL"); | ||
600 | |||
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 9ef32b3df91..647c6add219 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c | |||
@@ -18,12 +18,13 @@ | |||
18 | #include <asm/prom.h> | 18 | #include <asm/prom.h> |
19 | #include <asm/machdep.h> | 19 | #include <asm/machdep.h> |
20 | #include <asm/io.h> | 20 | #include <asm/io.h> |
21 | #include <asm/system.h> | ||
21 | #include <asm/sections.h> | 22 | #include <asm/sections.h> |
22 | #include <asm/pmac_low_i2c.h> | 23 | #include <asm/pmac_low_i2c.h> |
23 | 24 | ||
24 | #include "windfarm.h" | 25 | #include "windfarm.h" |
25 | 26 | ||
26 | #define VERSION "1.0" | 27 | #define VERSION "0.2" |
27 | 28 | ||
28 | #undef DEBUG | 29 | #undef DEBUG |
29 | 30 | ||
@@ -36,8 +37,8 @@ | |||
36 | struct wf_lm75_sensor { | 37 | struct wf_lm75_sensor { |
37 | int ds1775 : 1; | 38 | int ds1775 : 1; |
38 | int inited : 1; | 39 | int inited : 1; |
39 | struct i2c_client *i2c; | 40 | struct i2c_client *i2c; |
40 | struct wf_sensor sens; | 41 | struct wf_sensor sens; |
41 | }; | 42 | }; |
42 | #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) | 43 | #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) |
43 | 44 | ||
@@ -90,19 +91,40 @@ static struct wf_sensor_ops wf_lm75_ops = { | |||
90 | 91 | ||
91 | static int wf_lm75_probe(struct i2c_client *client, | 92 | static int wf_lm75_probe(struct i2c_client *client, |
92 | const struct i2c_device_id *id) | 93 | const struct i2c_device_id *id) |
93 | { | 94 | { |
94 | struct wf_lm75_sensor *lm; | 95 | struct wf_lm75_sensor *lm; |
95 | int rc, ds1775 = id->driver_data; | 96 | int rc; |
96 | const char *name, *loc; | ||
97 | 97 | ||
98 | DBG("wf_lm75: creating %s device at address 0x%02x\n", | 98 | lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); |
99 | ds1775 ? "ds1775" : "lm75", client->addr); | 99 | if (lm == NULL) |
100 | return -ENODEV; | ||
100 | 101 | ||
101 | loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); | 102 | lm->inited = 0; |
102 | if (!loc) { | 103 | lm->ds1775 = id->driver_data; |
103 | dev_warn(&client->dev, "Missing hwsensor-location property!\n"); | 104 | lm->i2c = client; |
104 | return -ENXIO; | 105 | lm->sens.name = client->dev.platform_data; |
105 | } | 106 | lm->sens.ops = &wf_lm75_ops; |
107 | i2c_set_clientdata(client, lm); | ||
108 | |||
109 | rc = wf_register_sensor(&lm->sens); | ||
110 | if (rc) | ||
111 | kfree(lm); | ||
112 | |||
113 | return rc; | ||
114 | } | ||
115 | |||
116 | static struct i2c_driver wf_lm75_driver; | ||
117 | |||
118 | static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, | ||
119 | u8 addr, int ds1775, | ||
120 | const char *loc) | ||
121 | { | ||
122 | struct i2c_board_info info; | ||
123 | struct i2c_client *client; | ||
124 | char *name; | ||
125 | |||
126 | DBG("wf_lm75: creating %s device at address 0x%02x\n", | ||
127 | ds1775 ? "ds1775" : "lm75", addr); | ||
106 | 128 | ||
107 | /* Usual rant about sensor names not beeing very consistent in | 129 | /* Usual rant about sensor names not beeing very consistent in |
108 | * the device-tree, oh well ... | 130 | * the device-tree, oh well ... |
@@ -116,31 +138,68 @@ static int wf_lm75_probe(struct i2c_client *client, | |||
116 | name = "optical-drive-temp"; | 138 | name = "optical-drive-temp"; |
117 | else if (!strcmp(loc, "HD Temp")) | 139 | else if (!strcmp(loc, "HD Temp")) |
118 | name = "hard-drive-temp"; | 140 | name = "hard-drive-temp"; |
119 | else if (!strcmp(loc, "PCI SLOTS")) | ||
120 | name = "slots-temp"; | ||
121 | else if (!strcmp(loc, "CPU A INLET")) | ||
122 | name = "cpu-inlet-temp-0"; | ||
123 | else if (!strcmp(loc, "CPU B INLET")) | ||
124 | name = "cpu-inlet-temp-1"; | ||
125 | else | 141 | else |
126 | return -ENXIO; | 142 | goto fail; |
127 | 143 | ||
144 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
145 | info.addr = (addr >> 1) & 0x7f; | ||
146 | info.platform_data = name; | ||
147 | strlcpy(info.type, ds1775 ? "wf_ds1775" : "wf_lm75", I2C_NAME_SIZE); | ||
148 | |||
149 | client = i2c_new_device(adapter, &info); | ||
150 | if (client == NULL) { | ||
151 | printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n", | ||
152 | ds1775 ? "ds1775" : "lm75", name); | ||
153 | goto fail; | ||
154 | } | ||
128 | 155 | ||
129 | lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); | 156 | /* |
130 | if (lm == NULL) | 157 | * Let i2c-core delete that device on driver removal. |
158 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
159 | */ | ||
160 | list_add_tail(&client->detected, &wf_lm75_driver.clients); | ||
161 | return client; | ||
162 | fail: | ||
163 | return NULL; | ||
164 | } | ||
165 | |||
166 | static int wf_lm75_attach(struct i2c_adapter *adapter) | ||
167 | { | ||
168 | struct device_node *busnode, *dev; | ||
169 | struct pmac_i2c_bus *bus; | ||
170 | |||
171 | DBG("wf_lm75: adapter %s detected\n", adapter->name); | ||
172 | |||
173 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
174 | if (bus == NULL) | ||
131 | return -ENODEV; | 175 | return -ENODEV; |
176 | busnode = pmac_i2c_get_bus_node(bus); | ||
132 | 177 | ||
133 | lm->inited = 0; | 178 | DBG("wf_lm75: bus found, looking for device...\n"); |
134 | lm->ds1775 = ds1775; | ||
135 | lm->i2c = client; | ||
136 | lm->sens.name = (char *)name; /* XXX fix constness in structure */ | ||
137 | lm->sens.ops = &wf_lm75_ops; | ||
138 | i2c_set_clientdata(client, lm); | ||
139 | 179 | ||
140 | rc = wf_register_sensor(&lm->sens); | 180 | /* Now look for lm75(s) in there */ |
141 | if (rc) | 181 | for (dev = NULL; |
142 | kfree(lm); | 182 | (dev = of_get_next_child(busnode, dev)) != NULL;) { |
143 | return rc; | 183 | const char *loc = |
184 | of_get_property(dev, "hwsensor-location", NULL); | ||
185 | u8 addr; | ||
186 | |||
187 | /* We must re-match the adapter in order to properly check | ||
188 | * the channel on multibus setups | ||
189 | */ | ||
190 | if (!pmac_i2c_match_adapter(dev, adapter)) | ||
191 | continue; | ||
192 | addr = pmac_i2c_get_dev_addr(dev); | ||
193 | if (loc == NULL || addr == 0) | ||
194 | continue; | ||
195 | /* real lm75 */ | ||
196 | if (of_device_is_compatible(dev, "lm75")) | ||
197 | wf_lm75_create(adapter, addr, 0, loc); | ||
198 | /* ds1775 (compatible, better resolution */ | ||
199 | else if (of_device_is_compatible(dev, "ds1775")) | ||
200 | wf_lm75_create(adapter, addr, 1, loc); | ||
201 | } | ||
202 | return 0; | ||
144 | } | 203 | } |
145 | 204 | ||
146 | static int wf_lm75_remove(struct i2c_client *client) | 205 | static int wf_lm75_remove(struct i2c_client *client) |
@@ -159,22 +218,39 @@ static int wf_lm75_remove(struct i2c_client *client) | |||
159 | } | 218 | } |
160 | 219 | ||
161 | static const struct i2c_device_id wf_lm75_id[] = { | 220 | static const struct i2c_device_id wf_lm75_id[] = { |
162 | { "MAC,lm75", 0 }, | 221 | { "wf_lm75", 0 }, |
163 | { "MAC,ds1775", 1 }, | 222 | { "wf_ds1775", 1 }, |
164 | { } | 223 | { } |
165 | }; | 224 | }; |
166 | MODULE_DEVICE_TABLE(i2c, wf_lm75_id); | ||
167 | 225 | ||
168 | static struct i2c_driver wf_lm75_driver = { | 226 | static struct i2c_driver wf_lm75_driver = { |
169 | .driver = { | 227 | .driver = { |
170 | .name = "wf_lm75", | 228 | .name = "wf_lm75", |
171 | }, | 229 | }, |
230 | .attach_adapter = wf_lm75_attach, | ||
172 | .probe = wf_lm75_probe, | 231 | .probe = wf_lm75_probe, |
173 | .remove = wf_lm75_remove, | 232 | .remove = wf_lm75_remove, |
174 | .id_table = wf_lm75_id, | 233 | .id_table = wf_lm75_id, |
175 | }; | 234 | }; |
176 | 235 | ||
177 | module_i2c_driver(wf_lm75_driver); | 236 | static int __init wf_lm75_sensor_init(void) |
237 | { | ||
238 | /* Don't register on old machines that use therm_pm72 for now */ | ||
239 | if (of_machine_is_compatible("PowerMac7,2") || | ||
240 | of_machine_is_compatible("PowerMac7,3") || | ||
241 | of_machine_is_compatible("RackMac3,1")) | ||
242 | return -ENODEV; | ||
243 | return i2c_add_driver(&wf_lm75_driver); | ||
244 | } | ||
245 | |||
246 | static void __exit wf_lm75_sensor_exit(void) | ||
247 | { | ||
248 | i2c_del_driver(&wf_lm75_driver); | ||
249 | } | ||
250 | |||
251 | |||
252 | module_init(wf_lm75_sensor_init); | ||
253 | module_exit(wf_lm75_sensor_exit); | ||
178 | 254 | ||
179 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | 255 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); |
180 | MODULE_DESCRIPTION("LM75 sensor objects for PowerMacs thermal control"); | 256 | MODULE_DESCRIPTION("LM75 sensor objects for PowerMacs thermal control"); |
diff --git a/drivers/macintosh/windfarm_lm87_sensor.c b/drivers/macintosh/windfarm_lm87_sensor.c deleted file mode 100644 index c071aab79dd..00000000000 --- a/drivers/macintosh/windfarm_lm87_sensor.c +++ /dev/null | |||
@@ -1,201 +0,0 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. LM87 sensor | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/types.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/wait.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <asm/prom.h> | ||
19 | #include <asm/machdep.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/sections.h> | ||
22 | #include <asm/pmac_low_i2c.h> | ||
23 | |||
24 | #include "windfarm.h" | ||
25 | |||
26 | #define VERSION "1.0" | ||
27 | |||
28 | #undef DEBUG | ||
29 | |||
30 | #ifdef DEBUG | ||
31 | #define DBG(args...) printk(args) | ||
32 | #else | ||
33 | #define DBG(args...) do { } while(0) | ||
34 | #endif | ||
35 | |||
36 | struct wf_lm87_sensor { | ||
37 | struct i2c_client *i2c; | ||
38 | struct wf_sensor sens; | ||
39 | }; | ||
40 | #define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens) | ||
41 | |||
42 | |||
43 | static int wf_lm87_read_reg(struct i2c_client *chip, int reg) | ||
44 | { | ||
45 | int rc, tries = 0; | ||
46 | u8 buf; | ||
47 | |||
48 | for (;;) { | ||
49 | /* Set address */ | ||
50 | buf = (u8)reg; | ||
51 | rc = i2c_master_send(chip, &buf, 1); | ||
52 | if (rc <= 0) | ||
53 | goto error; | ||
54 | rc = i2c_master_recv(chip, &buf, 1); | ||
55 | if (rc <= 0) | ||
56 | goto error; | ||
57 | return (int)buf; | ||
58 | error: | ||
59 | DBG("wf_lm87: Error reading LM87, retrying...\n"); | ||
60 | if (++tries > 10) { | ||
61 | printk(KERN_ERR "wf_lm87: Error reading LM87 !\n"); | ||
62 | return -EIO; | ||
63 | } | ||
64 | msleep(10); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static int wf_lm87_get(struct wf_sensor *sr, s32 *value) | ||
69 | { | ||
70 | struct wf_lm87_sensor *lm = sr->priv; | ||
71 | s32 temp; | ||
72 | |||
73 | if (lm->i2c == NULL) | ||
74 | return -ENODEV; | ||
75 | |||
76 | #define LM87_INT_TEMP 0x27 | ||
77 | |||
78 | /* Read temperature register */ | ||
79 | temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP); | ||
80 | if (temp < 0) | ||
81 | return temp; | ||
82 | *value = temp << 16; | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static void wf_lm87_release(struct wf_sensor *sr) | ||
88 | { | ||
89 | struct wf_lm87_sensor *lm = wf_to_lm87(sr); | ||
90 | |||
91 | kfree(lm); | ||
92 | } | ||
93 | |||
94 | static struct wf_sensor_ops wf_lm87_ops = { | ||
95 | .get_value = wf_lm87_get, | ||
96 | .release = wf_lm87_release, | ||
97 | .owner = THIS_MODULE, | ||
98 | }; | ||
99 | |||
100 | static int wf_lm87_probe(struct i2c_client *client, | ||
101 | const struct i2c_device_id *id) | ||
102 | { | ||
103 | struct wf_lm87_sensor *lm; | ||
104 | const char *name = NULL, *loc; | ||
105 | struct device_node *np = NULL; | ||
106 | int rc; | ||
107 | |||
108 | /* | ||
109 | * The lm87 contains a whole pile of sensors, additionally, | ||
110 | * the Xserve G5 has several lm87's. However, for now we only | ||
111 | * care about the internal temperature sensor | ||
112 | */ | ||
113 | while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) { | ||
114 | if (strcmp(np->name, "int-temp")) | ||
115 | continue; | ||
116 | loc = of_get_property(np, "location", NULL); | ||
117 | if (!loc) | ||
118 | continue; | ||
119 | if (strstr(loc, "DIMM")) | ||
120 | name = "dimms-temp"; | ||
121 | else if (strstr(loc, "Processors")) | ||
122 | name = "between-cpus-temp"; | ||
123 | if (name) { | ||
124 | of_node_put(np); | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | if (!name) { | ||
129 | pr_warning("wf_lm87: Unsupported sensor %s\n", | ||
130 | client->dev.of_node->full_name); | ||
131 | return -ENODEV; | ||
132 | } | ||
133 | |||
134 | lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL); | ||
135 | if (lm == NULL) | ||
136 | return -ENODEV; | ||
137 | |||
138 | lm->i2c = client; | ||
139 | lm->sens.name = name; | ||
140 | lm->sens.ops = &wf_lm87_ops; | ||
141 | lm->sens.priv = lm; | ||
142 | i2c_set_clientdata(client, lm); | ||
143 | |||
144 | rc = wf_register_sensor(&lm->sens); | ||
145 | if (rc) | ||
146 | kfree(lm); | ||
147 | return rc; | ||
148 | } | ||
149 | |||
150 | static int wf_lm87_remove(struct i2c_client *client) | ||
151 | { | ||
152 | struct wf_lm87_sensor *lm = i2c_get_clientdata(client); | ||
153 | |||
154 | DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name); | ||
155 | |||
156 | /* Mark client detached */ | ||
157 | lm->i2c = NULL; | ||
158 | |||
159 | /* release sensor */ | ||
160 | wf_unregister_sensor(&lm->sens); | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static const struct i2c_device_id wf_lm87_id[] = { | ||
166 | { "MAC,lm87cimt", 0 }, | ||
167 | { } | ||
168 | }; | ||
169 | MODULE_DEVICE_TABLE(i2c, wf_lm87_id); | ||
170 | |||
171 | static struct i2c_driver wf_lm87_driver = { | ||
172 | .driver = { | ||
173 | .name = "wf_lm87", | ||
174 | }, | ||
175 | .probe = wf_lm87_probe, | ||
176 | .remove = wf_lm87_remove, | ||
177 | .id_table = wf_lm87_id, | ||
178 | }; | ||
179 | |||
180 | static int __init wf_lm87_sensor_init(void) | ||
181 | { | ||
182 | /* We only support this on the Xserve */ | ||
183 | if (!of_machine_is_compatible("RackMac3,1")) | ||
184 | return -ENODEV; | ||
185 | |||
186 | return i2c_add_driver(&wf_lm87_driver); | ||
187 | } | ||
188 | |||
189 | static void __exit wf_lm87_sensor_exit(void) | ||
190 | { | ||
191 | i2c_del_driver(&wf_lm87_driver); | ||
192 | } | ||
193 | |||
194 | |||
195 | module_init(wf_lm87_sensor_init); | ||
196 | module_exit(wf_lm87_sensor_exit); | ||
197 | |||
198 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
199 | MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control"); | ||
200 | MODULE_LICENSE("GPL"); | ||
201 | |||
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index 945a25b2f31..8204113268f 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c | |||
@@ -16,7 +16,7 @@ | |||
16 | 16 | ||
17 | #include "windfarm.h" | 17 | #include "windfarm.h" |
18 | 18 | ||
19 | #define VERSION "1.0" | 19 | #define VERSION "0.2" |
20 | 20 | ||
21 | /* This currently only exports the external temperature sensor, | 21 | /* This currently only exports the external temperature sensor, |
22 | since that's all the control loops need. */ | 22 | since that's all the control loops need. */ |
@@ -64,29 +64,9 @@ static struct wf_sensor_ops wf_max6690_ops = { | |||
64 | static int wf_max6690_probe(struct i2c_client *client, | 64 | static int wf_max6690_probe(struct i2c_client *client, |
65 | const struct i2c_device_id *id) | 65 | const struct i2c_device_id *id) |
66 | { | 66 | { |
67 | const char *name, *loc; | ||
68 | struct wf_6690_sensor *max; | 67 | struct wf_6690_sensor *max; |
69 | int rc; | 68 | int rc; |
70 | 69 | ||
71 | loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); | ||
72 | if (!loc) { | ||
73 | dev_warn(&client->dev, "Missing hwsensor-location property!\n"); | ||
74 | return -ENXIO; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * We only expose the external temperature register for | ||
79 | * now as this is all we need for our control loops | ||
80 | */ | ||
81 | if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT")) | ||
82 | name = "backside-temp"; | ||
83 | else if (!strcmp(loc, "NB Ambient")) | ||
84 | name = "north-bridge-temp"; | ||
85 | else if (!strcmp(loc, "GPU Ambient")) | ||
86 | name = "gpu-temp"; | ||
87 | else | ||
88 | return -ENXIO; | ||
89 | |||
90 | max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); | 70 | max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); |
91 | if (max == NULL) { | 71 | if (max == NULL) { |
92 | printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " | 72 | printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " |
@@ -95,16 +75,90 @@ static int wf_max6690_probe(struct i2c_client *client, | |||
95 | } | 75 | } |
96 | 76 | ||
97 | max->i2c = client; | 77 | max->i2c = client; |
98 | max->sens.name = (char *)name; /* XXX fix constness in structure */ | 78 | max->sens.name = client->dev.platform_data; |
99 | max->sens.ops = &wf_max6690_ops; | 79 | max->sens.ops = &wf_max6690_ops; |
100 | i2c_set_clientdata(client, max); | 80 | i2c_set_clientdata(client, max); |
101 | 81 | ||
102 | rc = wf_register_sensor(&max->sens); | 82 | rc = wf_register_sensor(&max->sens); |
103 | if (rc) | 83 | if (rc) { |
104 | kfree(max); | 84 | kfree(max); |
85 | } | ||
86 | |||
105 | return rc; | 87 | return rc; |
106 | } | 88 | } |
107 | 89 | ||
90 | static struct i2c_driver wf_max6690_driver; | ||
91 | |||
92 | static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter, | ||
93 | u8 addr, const char *loc) | ||
94 | { | ||
95 | struct i2c_board_info info; | ||
96 | struct i2c_client *client; | ||
97 | char *name; | ||
98 | |||
99 | if (!strcmp(loc, "BACKSIDE")) | ||
100 | name = "backside-temp"; | ||
101 | else if (!strcmp(loc, "NB Ambient")) | ||
102 | name = "north-bridge-temp"; | ||
103 | else if (!strcmp(loc, "GPU Ambient")) | ||
104 | name = "gpu-temp"; | ||
105 | else | ||
106 | goto fail; | ||
107 | |||
108 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
109 | info.addr = addr >> 1; | ||
110 | info.platform_data = name; | ||
111 | strlcpy(info.type, "wf_max6690", I2C_NAME_SIZE); | ||
112 | |||
113 | client = i2c_new_device(adapter, &info); | ||
114 | if (client == NULL) { | ||
115 | printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n"); | ||
116 | goto fail; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Let i2c-core delete that device on driver removal. | ||
121 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
122 | */ | ||
123 | list_add_tail(&client->detected, &wf_max6690_driver.clients); | ||
124 | return client; | ||
125 | |||
126 | fail: | ||
127 | return NULL; | ||
128 | } | ||
129 | |||
130 | static int wf_max6690_attach(struct i2c_adapter *adapter) | ||
131 | { | ||
132 | struct device_node *busnode, *dev = NULL; | ||
133 | struct pmac_i2c_bus *bus; | ||
134 | const char *loc; | ||
135 | |||
136 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
137 | if (bus == NULL) | ||
138 | return -ENODEV; | ||
139 | busnode = pmac_i2c_get_bus_node(bus); | ||
140 | |||
141 | while ((dev = of_get_next_child(busnode, dev)) != NULL) { | ||
142 | u8 addr; | ||
143 | |||
144 | /* We must re-match the adapter in order to properly check | ||
145 | * the channel on multibus setups | ||
146 | */ | ||
147 | if (!pmac_i2c_match_adapter(dev, adapter)) | ||
148 | continue; | ||
149 | if (!of_device_is_compatible(dev, "max6690")) | ||
150 | continue; | ||
151 | addr = pmac_i2c_get_dev_addr(dev); | ||
152 | loc = of_get_property(dev, "hwsensor-location", NULL); | ||
153 | if (loc == NULL || addr == 0) | ||
154 | continue; | ||
155 | printk("found max6690, loc=%s addr=0x%02x\n", loc, addr); | ||
156 | wf_max6690_create(adapter, addr, loc); | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
108 | static int wf_max6690_remove(struct i2c_client *client) | 162 | static int wf_max6690_remove(struct i2c_client *client) |
109 | { | 163 | { |
110 | struct wf_6690_sensor *max = i2c_get_clientdata(client); | 164 | struct wf_6690_sensor *max = i2c_get_clientdata(client); |
@@ -116,21 +170,37 @@ static int wf_max6690_remove(struct i2c_client *client) | |||
116 | } | 170 | } |
117 | 171 | ||
118 | static const struct i2c_device_id wf_max6690_id[] = { | 172 | static const struct i2c_device_id wf_max6690_id[] = { |
119 | { "MAC,max6690", 0 }, | 173 | { "wf_max6690", 0 }, |
120 | { } | 174 | { } |
121 | }; | 175 | }; |
122 | MODULE_DEVICE_TABLE(i2c, wf_max6690_id); | ||
123 | 176 | ||
124 | static struct i2c_driver wf_max6690_driver = { | 177 | static struct i2c_driver wf_max6690_driver = { |
125 | .driver = { | 178 | .driver = { |
126 | .name = "wf_max6690", | 179 | .name = "wf_max6690", |
127 | }, | 180 | }, |
181 | .attach_adapter = wf_max6690_attach, | ||
128 | .probe = wf_max6690_probe, | 182 | .probe = wf_max6690_probe, |
129 | .remove = wf_max6690_remove, | 183 | .remove = wf_max6690_remove, |
130 | .id_table = wf_max6690_id, | 184 | .id_table = wf_max6690_id, |
131 | }; | 185 | }; |
132 | 186 | ||
133 | module_i2c_driver(wf_max6690_driver); | 187 | static int __init wf_max6690_sensor_init(void) |
188 | { | ||
189 | /* Don't register on old machines that use therm_pm72 for now */ | ||
190 | if (of_machine_is_compatible("PowerMac7,2") || | ||
191 | of_machine_is_compatible("PowerMac7,3") || | ||
192 | of_machine_is_compatible("RackMac3,1")) | ||
193 | return -ENODEV; | ||
194 | return i2c_add_driver(&wf_max6690_driver); | ||
195 | } | ||
196 | |||
197 | static void __exit wf_max6690_sensor_exit(void) | ||
198 | { | ||
199 | i2c_del_driver(&wf_max6690_driver); | ||
200 | } | ||
201 | |||
202 | module_init(wf_max6690_sensor_init); | ||
203 | module_exit(wf_max6690_sensor_exit); | ||
134 | 204 | ||
135 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); | 205 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); |
136 | MODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control"); | 206 | MODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control"); |
diff --git a/drivers/macintosh/windfarm_mpu.h b/drivers/macintosh/windfarm_mpu.h deleted file mode 100644 index 046edc8c2ec..00000000000 --- a/drivers/macintosh/windfarm_mpu.h +++ /dev/null | |||
@@ -1,105 +0,0 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control | ||
3 | * | ||
4 | * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. | ||
5 | * | ||
6 | * Released under the term of the GNU GPL v2. | ||
7 | */ | ||
8 | |||
9 | #ifndef __WINDFARM_MPU_H | ||
10 | #define __WINDFARM_MPU_H | ||
11 | |||
12 | typedef unsigned short fu16; | ||
13 | typedef int fs32; | ||
14 | typedef short fs16; | ||
15 | |||
16 | /* Definition of the MPU data structure which contains per CPU | ||
17 | * calibration information (among others) for the G5 machines | ||
18 | */ | ||
19 | struct mpu_data | ||
20 | { | ||
21 | u8 signature; /* 0x00 - EEPROM sig. */ | ||
22 | u8 bytes_used; /* 0x01 - Bytes used in eeprom (160 ?) */ | ||
23 | u8 size; /* 0x02 - EEPROM size (256 ?) */ | ||
24 | u8 version; /* 0x03 - EEPROM version */ | ||
25 | u32 data_revision; /* 0x04 - Dataset revision */ | ||
26 | u8 processor_bin_code[3]; /* 0x08 - Processor BIN code */ | ||
27 | u8 bin_code_expansion; /* 0x0b - ??? (padding ?) */ | ||
28 | u8 processor_num; /* 0x0c - Number of CPUs on this MPU */ | ||
29 | u8 input_mul_bus_div; /* 0x0d - Clock input multiplier/bus divider */ | ||
30 | u8 reserved1[2]; /* 0x0e - */ | ||
31 | u32 input_clk_freq_high; /* 0x10 - Input clock frequency high */ | ||
32 | u8 cpu_nb_target_cycles; /* 0x14 - ??? */ | ||
33 | u8 cpu_statlat; /* 0x15 - ??? */ | ||
34 | u8 cpu_snooplat; /* 0x16 - ??? */ | ||
35 | u8 cpu_snoopacc; /* 0x17 - ??? */ | ||
36 | u8 nb_paamwin; /* 0x18 - ??? */ | ||
37 | u8 nb_statlat; /* 0x19 - ??? */ | ||
38 | u8 nb_snooplat; /* 0x1a - ??? */ | ||
39 | u8 nb_snoopwin; /* 0x1b - ??? */ | ||
40 | u8 api_bus_mode; /* 0x1c - ??? */ | ||
41 | u8 reserved2[3]; /* 0x1d - */ | ||
42 | u32 input_clk_freq_low; /* 0x20 - Input clock frequency low */ | ||
43 | u8 processor_card_slot; /* 0x24 - Processor card slot number */ | ||
44 | u8 reserved3[2]; /* 0x25 - */ | ||
45 | u8 padjmax; /* 0x27 - Max power adjustment (Not in OF!) */ | ||
46 | u8 ttarget; /* 0x28 - Target temperature */ | ||
47 | u8 tmax; /* 0x29 - Max temperature */ | ||
48 | u8 pmaxh; /* 0x2a - Max power */ | ||
49 | u8 tguardband; /* 0x2b - Guardband temp ??? Hist. len in OSX */ | ||
50 | fs32 pid_gp; /* 0x2c - PID proportional gain */ | ||
51 | fs32 pid_gr; /* 0x30 - PID reset gain */ | ||
52 | fs32 pid_gd; /* 0x34 - PID derivative gain */ | ||
53 | fu16 voph; /* 0x38 - Vop High */ | ||
54 | fu16 vopl; /* 0x3a - Vop Low */ | ||
55 | fs16 nactual_die; /* 0x3c - nActual Die */ | ||
56 | fs16 nactual_heatsink; /* 0x3e - nActual Heatsink */ | ||
57 | fs16 nactual_system; /* 0x40 - nActual System */ | ||
58 | u16 calibration_flags; /* 0x42 - Calibration flags */ | ||
59 | fu16 mdiode; /* 0x44 - Diode M value (scaling factor) */ | ||
60 | fs16 bdiode; /* 0x46 - Diode B value (offset) */ | ||
61 | fs32 theta_heat_sink; /* 0x48 - Theta heat sink */ | ||
62 | u16 rminn_intake_fan; /* 0x4c - Intake fan min RPM */ | ||
63 | u16 rmaxn_intake_fan; /* 0x4e - Intake fan max RPM */ | ||
64 | u16 rminn_exhaust_fan; /* 0x50 - Exhaust fan min RPM */ | ||
65 | u16 rmaxn_exhaust_fan; /* 0x52 - Exhaust fan max RPM */ | ||
66 | u8 processor_part_num[8]; /* 0x54 - Processor part number XX pumps min/max */ | ||
67 | u32 processor_lot_num; /* 0x5c - Processor lot number */ | ||
68 | u8 orig_card_sernum[0x10]; /* 0x60 - Card original serial number */ | ||
69 | u8 curr_card_sernum[0x10]; /* 0x70 - Card current serial number */ | ||
70 | u8 mlb_sernum[0x18]; /* 0x80 - MLB serial number */ | ||
71 | u32 checksum1; /* 0x98 - */ | ||
72 | u32 checksum2; /* 0x9c - */ | ||
73 | }; /* Total size = 0xa0 */ | ||
74 | |||
75 | static inline const struct mpu_data *wf_get_mpu(int cpu) | ||
76 | { | ||
77 | struct device_node *np; | ||
78 | char nodename[64]; | ||
79 | const void *data; | ||
80 | int len; | ||
81 | |||
82 | /* | ||
83 | * prom.c routine for finding a node by path is a bit brain dead | ||
84 | * and requires exact @xxx unit numbers. This is a bit ugly but | ||
85 | * will work for these machines | ||
86 | */ | ||
87 | sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0); | ||
88 | np = of_find_node_by_path(nodename); | ||
89 | if (!np) | ||
90 | return NULL; | ||
91 | data = of_get_property(np, "cpuid", &len); | ||
92 | of_node_put(np); | ||
93 | if (!data) | ||
94 | return NULL; | ||
95 | |||
96 | /* | ||
97 | * We are naughty, we have dropped the reference to the device | ||
98 | * node and still return a pointer to the content. We know we | ||
99 | * can do that though as this is only ever called on PowerMac | ||
100 | * which cannot remove those nodes | ||
101 | */ | ||
102 | return data; | ||
103 | } | ||
104 | |||
105 | #endif /* __WINDFARM_MPU_H */ | ||
diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c index 35ef6e2582b..e0ee80700cd 100644 --- a/drivers/macintosh/windfarm_pm112.c +++ b/drivers/macintosh/windfarm_pm112.c | |||
@@ -656,7 +656,7 @@ static int wf_pm112_probe(struct platform_device *dev) | |||
656 | return 0; | 656 | return 0; |
657 | } | 657 | } |
658 | 658 | ||
659 | static int wf_pm112_remove(struct platform_device *dev) | 659 | static int __devexit wf_pm112_remove(struct platform_device *dev) |
660 | { | 660 | { |
661 | wf_unregister_client(&pm112_events); | 661 | wf_unregister_client(&pm112_events); |
662 | /* should release all sensors and controls */ | 662 | /* should release all sensors and controls */ |
@@ -665,7 +665,7 @@ static int wf_pm112_remove(struct platform_device *dev) | |||
665 | 665 | ||
666 | static struct platform_driver wf_pm112_driver = { | 666 | static struct platform_driver wf_pm112_driver = { |
667 | .probe = wf_pm112_probe, | 667 | .probe = wf_pm112_probe, |
668 | .remove = wf_pm112_remove, | 668 | .remove = __devexit_p(wf_pm112_remove), |
669 | .driver = { | 669 | .driver = { |
670 | .name = "windfarm", | 670 | .name = "windfarm", |
671 | .owner = THIS_MODULE, | 671 | .owner = THIS_MODULE, |
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c index af605e915d4..30e6195e19d 100644 --- a/drivers/macintosh/windfarm_pm121.c +++ b/drivers/macintosh/windfarm_pm121.c | |||
@@ -215,6 +215,7 @@ | |||
215 | #include <asm/prom.h> | 215 | #include <asm/prom.h> |
216 | #include <asm/machdep.h> | 216 | #include <asm/machdep.h> |
217 | #include <asm/io.h> | 217 | #include <asm/io.h> |
218 | #include <asm/system.h> | ||
218 | #include <asm/sections.h> | 219 | #include <asm/sections.h> |
219 | #include <asm/smu.h> | 220 | #include <asm/smu.h> |
220 | 221 | ||
@@ -987,7 +988,7 @@ static int pm121_probe(struct platform_device *ddev) | |||
987 | return 0; | 988 | return 0; |
988 | } | 989 | } |
989 | 990 | ||
990 | static int pm121_remove(struct platform_device *ddev) | 991 | static int __devexit pm121_remove(struct platform_device *ddev) |
991 | { | 992 | { |
992 | wf_unregister_client(&pm121_events); | 993 | wf_unregister_client(&pm121_events); |
993 | return 0; | 994 | return 0; |
@@ -995,7 +996,7 @@ static int pm121_remove(struct platform_device *ddev) | |||
995 | 996 | ||
996 | static struct platform_driver pm121_driver = { | 997 | static struct platform_driver pm121_driver = { |
997 | .probe = pm121_probe, | 998 | .probe = pm121_probe, |
998 | .remove = pm121_remove, | 999 | .remove = __devexit_p(pm121_remove), |
999 | .driver = { | 1000 | .driver = { |
1000 | .name = "windfarm", | 1001 | .name = "windfarm", |
1001 | .bus = &platform_bus_type, | 1002 | .bus = &platform_bus_type, |
diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c deleted file mode 100644 index 6e5585357cd..00000000000 --- a/drivers/macintosh/windfarm_pm72.c +++ /dev/null | |||
@@ -1,847 +0,0 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. | ||
3 | * Control loops for PowerMac7,2 and 7,3 | ||
4 | * | ||
5 | * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. | ||
6 | * | ||
7 | * Use and redistribute under the terms of the GNU GPL v2. | ||
8 | */ | ||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/reboot.h> | ||
15 | #include <asm/prom.h> | ||
16 | #include <asm/smu.h> | ||
17 | |||
18 | #include "windfarm.h" | ||
19 | #include "windfarm_pid.h" | ||
20 | #include "windfarm_mpu.h" | ||
21 | |||
22 | #define VERSION "1.0" | ||
23 | |||
24 | #undef DEBUG | ||
25 | #undef LOTSA_DEBUG | ||
26 | |||
27 | #ifdef DEBUG | ||
28 | #define DBG(args...) printk(args) | ||
29 | #else | ||
30 | #define DBG(args...) do { } while(0) | ||
31 | #endif | ||
32 | |||
33 | #ifdef LOTSA_DEBUG | ||
34 | #define DBG_LOTS(args...) printk(args) | ||
35 | #else | ||
36 | #define DBG_LOTS(args...) do { } while(0) | ||
37 | #endif | ||
38 | |||
39 | /* define this to force CPU overtemp to 60 degree, useful for testing | ||
40 | * the overtemp code | ||
41 | */ | ||
42 | #undef HACKED_OVERTEMP | ||
43 | |||
44 | /* We currently only handle 2 chips */ | ||
45 | #define NR_CHIPS 2 | ||
46 | #define NR_CPU_FANS 3 * NR_CHIPS | ||
47 | |||
48 | /* Controls and sensors */ | ||
49 | static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; | ||
50 | static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; | ||
51 | static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; | ||
52 | static struct wf_sensor *backside_temp; | ||
53 | static struct wf_sensor *drives_temp; | ||
54 | |||
55 | static struct wf_control *cpu_front_fans[NR_CHIPS]; | ||
56 | static struct wf_control *cpu_rear_fans[NR_CHIPS]; | ||
57 | static struct wf_control *cpu_pumps[NR_CHIPS]; | ||
58 | static struct wf_control *backside_fan; | ||
59 | static struct wf_control *drives_fan; | ||
60 | static struct wf_control *slots_fan; | ||
61 | static struct wf_control *cpufreq_clamp; | ||
62 | |||
63 | /* We keep a temperature history for average calculation of 180s */ | ||
64 | #define CPU_TEMP_HIST_SIZE 180 | ||
65 | |||
66 | /* Fixed speed for slot fan */ | ||
67 | #define SLOTS_FAN_DEFAULT_PWM 40 | ||
68 | |||
69 | /* Scale value for CPU intake fans */ | ||
70 | #define CPU_INTAKE_SCALE 0x0000f852 | ||
71 | |||
72 | /* PID loop state */ | ||
73 | static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; | ||
74 | static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; | ||
75 | static bool cpu_pid_combined; | ||
76 | static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; | ||
77 | static int cpu_thist_pt; | ||
78 | static s64 cpu_thist_total; | ||
79 | static s32 cpu_all_tmax = 100 << 16; | ||
80 | static struct wf_pid_state backside_pid; | ||
81 | static int backside_tick; | ||
82 | static struct wf_pid_state drives_pid; | ||
83 | static int drives_tick; | ||
84 | |||
85 | static int nr_chips; | ||
86 | static bool have_all_controls; | ||
87 | static bool have_all_sensors; | ||
88 | static bool started; | ||
89 | |||
90 | static int failure_state; | ||
91 | #define FAILURE_SENSOR 1 | ||
92 | #define FAILURE_FAN 2 | ||
93 | #define FAILURE_PERM 4 | ||
94 | #define FAILURE_LOW_OVERTEMP 8 | ||
95 | #define FAILURE_HIGH_OVERTEMP 16 | ||
96 | |||
97 | /* Overtemp values */ | ||
98 | #define LOW_OVER_AVERAGE 0 | ||
99 | #define LOW_OVER_IMMEDIATE (10 << 16) | ||
100 | #define LOW_OVER_CLEAR ((-10) << 16) | ||
101 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
102 | #define HIGH_OVER_AVERAGE (10 << 16) | ||
103 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
104 | |||
105 | |||
106 | static void cpu_max_all_fans(void) | ||
107 | { | ||
108 | int i; | ||
109 | |||
110 | /* We max all CPU fans in case of a sensor error. We also do the | ||
111 | * cpufreq clamping now, even if it's supposedly done later by the | ||
112 | * generic code anyway, we do it earlier here to react faster | ||
113 | */ | ||
114 | if (cpufreq_clamp) | ||
115 | wf_control_set_max(cpufreq_clamp); | ||
116 | for (i = 0; i < nr_chips; i++) { | ||
117 | if (cpu_front_fans[i]) | ||
118 | wf_control_set_max(cpu_front_fans[i]); | ||
119 | if (cpu_rear_fans[i]) | ||
120 | wf_control_set_max(cpu_rear_fans[i]); | ||
121 | if (cpu_pumps[i]) | ||
122 | wf_control_set_max(cpu_pumps[i]); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static int cpu_check_overtemp(s32 temp) | ||
127 | { | ||
128 | int new_state = 0; | ||
129 | s32 t_avg, t_old; | ||
130 | static bool first = true; | ||
131 | |||
132 | /* First check for immediate overtemps */ | ||
133 | if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { | ||
134 | new_state |= FAILURE_LOW_OVERTEMP; | ||
135 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
136 | printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" | ||
137 | " temperature !\n"); | ||
138 | } | ||
139 | if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { | ||
140 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
141 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
142 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
143 | " immediate CPU temperature !\n"); | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * The first time around, initialize the array with the first | ||
148 | * temperature reading | ||
149 | */ | ||
150 | if (first) { | ||
151 | int i; | ||
152 | |||
153 | cpu_thist_total = 0; | ||
154 | for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { | ||
155 | cpu_thist[i] = temp; | ||
156 | cpu_thist_total += temp; | ||
157 | } | ||
158 | first = false; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * We calculate a history of max temperatures and use that for the | ||
163 | * overtemp management | ||
164 | */ | ||
165 | t_old = cpu_thist[cpu_thist_pt]; | ||
166 | cpu_thist[cpu_thist_pt] = temp; | ||
167 | cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; | ||
168 | cpu_thist_total -= t_old; | ||
169 | cpu_thist_total += temp; | ||
170 | t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; | ||
171 | |||
172 | DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", | ||
173 | FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); | ||
174 | |||
175 | /* Now check for average overtemps */ | ||
176 | if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { | ||
177 | new_state |= FAILURE_LOW_OVERTEMP; | ||
178 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
179 | printk(KERN_ERR "windfarm: Overtemp due to average CPU" | ||
180 | " temperature !\n"); | ||
181 | } | ||
182 | if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { | ||
183 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
184 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
185 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
186 | " average CPU temperature !\n"); | ||
187 | } | ||
188 | |||
189 | /* Now handle overtemp conditions. We don't currently use the windfarm | ||
190 | * overtemp handling core as it's not fully suited to the needs of those | ||
191 | * new machine. This will be fixed later. | ||
192 | */ | ||
193 | if (new_state) { | ||
194 | /* High overtemp -> immediate shutdown */ | ||
195 | if (new_state & FAILURE_HIGH_OVERTEMP) | ||
196 | machine_power_off(); | ||
197 | if ((failure_state & new_state) != new_state) | ||
198 | cpu_max_all_fans(); | ||
199 | failure_state |= new_state; | ||
200 | } else if ((failure_state & FAILURE_LOW_OVERTEMP) && | ||
201 | (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { | ||
202 | printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); | ||
203 | failure_state &= ~FAILURE_LOW_OVERTEMP; | ||
204 | } | ||
205 | |||
206 | return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); | ||
207 | } | ||
208 | |||
209 | static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) | ||
210 | { | ||
211 | s32 dtemp, volts, amps; | ||
212 | int rc; | ||
213 | |||
214 | /* Get diode temperature */ | ||
215 | rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); | ||
216 | if (rc) { | ||
217 | DBG(" CPU%d: temp reading error !\n", cpu); | ||
218 | return -EIO; | ||
219 | } | ||
220 | DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); | ||
221 | *temp = dtemp; | ||
222 | |||
223 | /* Get voltage */ | ||
224 | rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); | ||
225 | if (rc) { | ||
226 | DBG(" CPU%d, volts reading error !\n", cpu); | ||
227 | return -EIO; | ||
228 | } | ||
229 | DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); | ||
230 | |||
231 | /* Get current */ | ||
232 | rc = wf_sensor_get(sens_cpu_amps[cpu], &s); | ||
233 | if (rc) { | ||
234 | DBG(" CPU%d, current reading error !\n", cpu); | ||
235 | return -EIO; | ||
236 | } | ||
237 | DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); | ||
238 | |||
239 | /* Calculate power */ | ||
240 | |||
241 | /* Scale voltage and current raw sensor values according to fixed scales | ||
242 | * obtained in Darwin and calculate power from I and V | ||
243 | */ | ||
244 | *power = (((u64)volts) * ((u64)amps)) >> 16; | ||
245 | |||
246 | DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); | ||
247 | |||
248 | return 0; | ||
249 | |||
250 | } | ||
251 | |||
252 | static void cpu_fans_tick_split(void) | ||
253 | { | ||
254 | int err, cpu; | ||
255 | s32 intake, temp, power, t_max = 0; | ||
256 | |||
257 | DBG_LOTS("* cpu fans_tick_split()\n"); | ||
258 | |||
259 | for (cpu = 0; cpu < nr_chips; ++cpu) { | ||
260 | struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; | ||
261 | |||
262 | /* Read current speed */ | ||
263 | wf_control_get(cpu_rear_fans[cpu], &sp->target); | ||
264 | |||
265 | DBG_LOTS(" CPU%d: cur_target = %d RPM\n", cpu, sp->target); | ||
266 | |||
267 | err = read_one_cpu_vals(cpu, &temp, &power); | ||
268 | if (err) { | ||
269 | failure_state |= FAILURE_SENSOR; | ||
270 | cpu_max_all_fans(); | ||
271 | return; | ||
272 | } | ||
273 | |||
274 | /* Keep track of highest temp */ | ||
275 | t_max = max(t_max, temp); | ||
276 | |||
277 | /* Handle possible overtemps */ | ||
278 | if (cpu_check_overtemp(t_max)) | ||
279 | return; | ||
280 | |||
281 | /* Run PID */ | ||
282 | wf_cpu_pid_run(sp, power, temp); | ||
283 | |||
284 | DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); | ||
285 | |||
286 | /* Apply result directly to exhaust fan */ | ||
287 | err = wf_control_set(cpu_rear_fans[cpu], sp->target); | ||
288 | if (err) { | ||
289 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
290 | cpu_rear_fans[cpu]->name, err); | ||
291 | failure_state |= FAILURE_FAN; | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | /* Scale result for intake fan */ | ||
296 | intake = (sp->target * CPU_INTAKE_SCALE) >> 16; | ||
297 | DBG_LOTS(" CPU%d: intake = %d RPM\n", cpu, intake); | ||
298 | err = wf_control_set(cpu_front_fans[cpu], intake); | ||
299 | if (err) { | ||
300 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
301 | cpu_front_fans[cpu]->name, err); | ||
302 | failure_state |= FAILURE_FAN; | ||
303 | break; | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | static void cpu_fans_tick_combined(void) | ||
309 | { | ||
310 | s32 temp0, power0, temp1, power1, t_max = 0; | ||
311 | s32 temp, power, intake, pump; | ||
312 | struct wf_control *pump0, *pump1; | ||
313 | struct wf_cpu_pid_state *sp = &cpu_pid[0]; | ||
314 | int err, cpu; | ||
315 | |||
316 | DBG_LOTS("* cpu fans_tick_combined()\n"); | ||
317 | |||
318 | /* Read current speed from cpu 0 */ | ||
319 | wf_control_get(cpu_rear_fans[0], &sp->target); | ||
320 | |||
321 | DBG_LOTS(" CPUs: cur_target = %d RPM\n", sp->target); | ||
322 | |||
323 | /* Read values for both CPUs */ | ||
324 | err = read_one_cpu_vals(0, &temp0, &power0); | ||
325 | if (err) { | ||
326 | failure_state |= FAILURE_SENSOR; | ||
327 | cpu_max_all_fans(); | ||
328 | return; | ||
329 | } | ||
330 | err = read_one_cpu_vals(1, &temp1, &power1); | ||
331 | if (err) { | ||
332 | failure_state |= FAILURE_SENSOR; | ||
333 | cpu_max_all_fans(); | ||
334 | return; | ||
335 | } | ||
336 | |||
337 | /* Keep track of highest temp */ | ||
338 | t_max = max(t_max, max(temp0, temp1)); | ||
339 | |||
340 | /* Handle possible overtemps */ | ||
341 | if (cpu_check_overtemp(t_max)) | ||
342 | return; | ||
343 | |||
344 | /* Use the max temp & power of both */ | ||
345 | temp = max(temp0, temp1); | ||
346 | power = max(power0, power1); | ||
347 | |||
348 | /* Run PID */ | ||
349 | wf_cpu_pid_run(sp, power, temp); | ||
350 | |||
351 | /* Scale result for intake fan */ | ||
352 | intake = (sp->target * CPU_INTAKE_SCALE) >> 16; | ||
353 | |||
354 | /* Same deal with pump speed */ | ||
355 | pump0 = cpu_pumps[0]; | ||
356 | pump1 = cpu_pumps[1]; | ||
357 | if (!pump0) { | ||
358 | pump0 = pump1; | ||
359 | pump1 = NULL; | ||
360 | } | ||
361 | pump = (sp->target * wf_control_get_max(pump0)) / | ||
362 | cpu_mpu_data[0]->rmaxn_exhaust_fan; | ||
363 | |||
364 | DBG_LOTS(" CPUs: target = %d RPM\n", sp->target); | ||
365 | DBG_LOTS(" CPUs: intake = %d RPM\n", intake); | ||
366 | DBG_LOTS(" CPUs: pump = %d RPM\n", pump); | ||
367 | |||
368 | for (cpu = 0; cpu < nr_chips; cpu++) { | ||
369 | err = wf_control_set(cpu_rear_fans[cpu], sp->target); | ||
370 | if (err) { | ||
371 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
372 | cpu_rear_fans[cpu]->name, err); | ||
373 | failure_state |= FAILURE_FAN; | ||
374 | } | ||
375 | err = wf_control_set(cpu_front_fans[cpu], intake); | ||
376 | if (err) { | ||
377 | pr_warning("wf_pm72: Fan %s reports error %d\n", | ||
378 | cpu_front_fans[cpu]->name, err); | ||
379 | failure_state |= FAILURE_FAN; | ||
380 | } | ||
381 | err = 0; | ||
382 | if (cpu_pumps[cpu]) | ||
383 | err = wf_control_set(cpu_pumps[cpu], pump); | ||
384 | if (err) { | ||
385 | pr_warning("wf_pm72: Pump %s reports error %d\n", | ||
386 | cpu_pumps[cpu]->name, err); | ||
387 | failure_state |= FAILURE_FAN; | ||
388 | } | ||
389 | } | ||
390 | } | ||
391 | |||
392 | /* Implementation... */ | ||
393 | static int cpu_setup_pid(int cpu) | ||
394 | { | ||
395 | struct wf_cpu_pid_param pid; | ||
396 | const struct mpu_data *mpu = cpu_mpu_data[cpu]; | ||
397 | s32 tmax, ttarget, ptarget; | ||
398 | int fmin, fmax, hsize; | ||
399 | |||
400 | /* Get PID params from the appropriate MPU EEPROM */ | ||
401 | tmax = mpu->tmax << 16; | ||
402 | ttarget = mpu->ttarget << 16; | ||
403 | ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; | ||
404 | |||
405 | DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", | ||
406 | cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); | ||
407 | |||
408 | /* We keep a global tmax for overtemp calculations */ | ||
409 | if (tmax < cpu_all_tmax) | ||
410 | cpu_all_tmax = tmax; | ||
411 | |||
412 | /* Set PID min/max by using the rear fan min/max */ | ||
413 | fmin = wf_control_get_min(cpu_rear_fans[cpu]); | ||
414 | fmax = wf_control_get_max(cpu_rear_fans[cpu]); | ||
415 | DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); | ||
416 | |||
417 | /* History size */ | ||
418 | hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); | ||
419 | DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); | ||
420 | |||
421 | /* Initialize PID loop */ | ||
422 | pid.interval = 1; /* seconds */ | ||
423 | pid.history_len = hsize; | ||
424 | pid.gd = mpu->pid_gd; | ||
425 | pid.gp = mpu->pid_gp; | ||
426 | pid.gr = mpu->pid_gr; | ||
427 | pid.tmax = tmax; | ||
428 | pid.ttarget = ttarget; | ||
429 | pid.pmaxadj = ptarget; | ||
430 | pid.min = fmin; | ||
431 | pid.max = fmax; | ||
432 | |||
433 | wf_cpu_pid_init(&cpu_pid[cpu], &pid); | ||
434 | cpu_pid[cpu].target = 1000; | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | /* Backside/U3 fan */ | ||
440 | static struct wf_pid_param backside_u3_param = { | ||
441 | .interval = 5, | ||
442 | .history_len = 2, | ||
443 | .gd = 40 << 20, | ||
444 | .gp = 5 << 20, | ||
445 | .gr = 0, | ||
446 | .itarget = 65 << 16, | ||
447 | .additive = 1, | ||
448 | .min = 20, | ||
449 | .max = 100, | ||
450 | }; | ||
451 | |||
452 | static struct wf_pid_param backside_u3h_param = { | ||
453 | .interval = 5, | ||
454 | .history_len = 2, | ||
455 | .gd = 20 << 20, | ||
456 | .gp = 5 << 20, | ||
457 | .gr = 0, | ||
458 | .itarget = 75 << 16, | ||
459 | .additive = 1, | ||
460 | .min = 20, | ||
461 | .max = 100, | ||
462 | }; | ||
463 | |||
464 | static void backside_fan_tick(void) | ||
465 | { | ||
466 | s32 temp; | ||
467 | int speed; | ||
468 | int err; | ||
469 | |||
470 | if (!backside_fan || !backside_temp || !backside_tick) | ||
471 | return; | ||
472 | if (--backside_tick > 0) | ||
473 | return; | ||
474 | backside_tick = backside_pid.param.interval; | ||
475 | |||
476 | DBG_LOTS("* backside fans tick\n"); | ||
477 | |||
478 | /* Update fan speed from actual fans */ | ||
479 | err = wf_control_get(backside_fan, &speed); | ||
480 | if (!err) | ||
481 | backside_pid.target = speed; | ||
482 | |||
483 | err = wf_sensor_get(backside_temp, &temp); | ||
484 | if (err) { | ||
485 | printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n", | ||
486 | err); | ||
487 | failure_state |= FAILURE_SENSOR; | ||
488 | wf_control_set_max(backside_fan); | ||
489 | return; | ||
490 | } | ||
491 | speed = wf_pid_run(&backside_pid, temp); | ||
492 | |||
493 | DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", | ||
494 | FIX32TOPRINT(temp), speed); | ||
495 | |||
496 | err = wf_control_set(backside_fan, speed); | ||
497 | if (err) { | ||
498 | printk(KERN_WARNING "windfarm: backside fan error %d\n", err); | ||
499 | failure_state |= FAILURE_FAN; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | static void backside_setup_pid(void) | ||
504 | { | ||
505 | /* first time initialize things */ | ||
506 | s32 fmin = wf_control_get_min(backside_fan); | ||
507 | s32 fmax = wf_control_get_max(backside_fan); | ||
508 | struct wf_pid_param param; | ||
509 | struct device_node *u3; | ||
510 | int u3h = 1; /* conservative by default */ | ||
511 | |||
512 | u3 = of_find_node_by_path("/u3@0,f8000000"); | ||
513 | if (u3 != NULL) { | ||
514 | const u32 *vers = of_get_property(u3, "device-rev", NULL); | ||
515 | if (vers) | ||
516 | if (((*vers) & 0x3f) < 0x34) | ||
517 | u3h = 0; | ||
518 | of_node_put(u3); | ||
519 | } | ||
520 | |||
521 | param = u3h ? backside_u3h_param : backside_u3_param; | ||
522 | |||
523 | param.min = max(param.min, fmin); | ||
524 | param.max = min(param.max, fmax); | ||
525 | wf_pid_init(&backside_pid, ¶m); | ||
526 | backside_tick = 1; | ||
527 | |||
528 | pr_info("wf_pm72: Backside control loop started.\n"); | ||
529 | } | ||
530 | |||
531 | /* Drive bay fan */ | ||
532 | static const struct wf_pid_param drives_param = { | ||
533 | .interval = 5, | ||
534 | .history_len = 2, | ||
535 | .gd = 30 << 20, | ||
536 | .gp = 5 << 20, | ||
537 | .gr = 0, | ||
538 | .itarget = 40 << 16, | ||
539 | .additive = 1, | ||
540 | .min = 300, | ||
541 | .max = 4000, | ||
542 | }; | ||
543 | |||
544 | static void drives_fan_tick(void) | ||
545 | { | ||
546 | s32 temp; | ||
547 | int speed; | ||
548 | int err; | ||
549 | |||
550 | if (!drives_fan || !drives_temp || !drives_tick) | ||
551 | return; | ||
552 | if (--drives_tick > 0) | ||
553 | return; | ||
554 | drives_tick = drives_pid.param.interval; | ||
555 | |||
556 | DBG_LOTS("* drives fans tick\n"); | ||
557 | |||
558 | /* Update fan speed from actual fans */ | ||
559 | err = wf_control_get(drives_fan, &speed); | ||
560 | if (!err) | ||
561 | drives_pid.target = speed; | ||
562 | |||
563 | err = wf_sensor_get(drives_temp, &temp); | ||
564 | if (err) { | ||
565 | pr_warning("wf_pm72: drive bay temp sensor error %d\n", err); | ||
566 | failure_state |= FAILURE_SENSOR; | ||
567 | wf_control_set_max(drives_fan); | ||
568 | return; | ||
569 | } | ||
570 | speed = wf_pid_run(&drives_pid, temp); | ||
571 | |||
572 | DBG_LOTS("drives PID temp=%d.%.3d speed=%d\n", | ||
573 | FIX32TOPRINT(temp), speed); | ||
574 | |||
575 | err = wf_control_set(drives_fan, speed); | ||
576 | if (err) { | ||
577 | printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err); | ||
578 | failure_state |= FAILURE_FAN; | ||
579 | } | ||
580 | } | ||
581 | |||
582 | static void drives_setup_pid(void) | ||
583 | { | ||
584 | /* first time initialize things */ | ||
585 | s32 fmin = wf_control_get_min(drives_fan); | ||
586 | s32 fmax = wf_control_get_max(drives_fan); | ||
587 | struct wf_pid_param param = drives_param; | ||
588 | |||
589 | param.min = max(param.min, fmin); | ||
590 | param.max = min(param.max, fmax); | ||
591 | wf_pid_init(&drives_pid, ¶m); | ||
592 | drives_tick = 1; | ||
593 | |||
594 | pr_info("wf_pm72: Drive bay control loop started.\n"); | ||
595 | } | ||
596 | |||
597 | static void set_fail_state(void) | ||
598 | { | ||
599 | cpu_max_all_fans(); | ||
600 | |||
601 | if (backside_fan) | ||
602 | wf_control_set_max(backside_fan); | ||
603 | if (slots_fan) | ||
604 | wf_control_set_max(slots_fan); | ||
605 | if (drives_fan) | ||
606 | wf_control_set_max(drives_fan); | ||
607 | } | ||
608 | |||
609 | static void pm72_tick(void) | ||
610 | { | ||
611 | int i, last_failure; | ||
612 | |||
613 | if (!started) { | ||
614 | started = 1; | ||
615 | printk(KERN_INFO "windfarm: CPUs control loops started.\n"); | ||
616 | for (i = 0; i < nr_chips; ++i) { | ||
617 | if (cpu_setup_pid(i) < 0) { | ||
618 | failure_state = FAILURE_PERM; | ||
619 | set_fail_state(); | ||
620 | break; | ||
621 | } | ||
622 | } | ||
623 | DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); | ||
624 | |||
625 | backside_setup_pid(); | ||
626 | drives_setup_pid(); | ||
627 | |||
628 | /* | ||
629 | * We don't have the right stuff to drive the PCI fan | ||
630 | * so we fix it to a default value | ||
631 | */ | ||
632 | wf_control_set(slots_fan, SLOTS_FAN_DEFAULT_PWM); | ||
633 | |||
634 | #ifdef HACKED_OVERTEMP | ||
635 | cpu_all_tmax = 60 << 16; | ||
636 | #endif | ||
637 | } | ||
638 | |||
639 | /* Permanent failure, bail out */ | ||
640 | if (failure_state & FAILURE_PERM) | ||
641 | return; | ||
642 | |||
643 | /* | ||
644 | * Clear all failure bits except low overtemp which will be eventually | ||
645 | * cleared by the control loop itself | ||
646 | */ | ||
647 | last_failure = failure_state; | ||
648 | failure_state &= FAILURE_LOW_OVERTEMP; | ||
649 | if (cpu_pid_combined) | ||
650 | cpu_fans_tick_combined(); | ||
651 | else | ||
652 | cpu_fans_tick_split(); | ||
653 | backside_fan_tick(); | ||
654 | drives_fan_tick(); | ||
655 | |||
656 | DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", | ||
657 | last_failure, failure_state); | ||
658 | |||
659 | /* Check for failures. Any failure causes cpufreq clamping */ | ||
660 | if (failure_state && last_failure == 0 && cpufreq_clamp) | ||
661 | wf_control_set_max(cpufreq_clamp); | ||
662 | if (failure_state == 0 && last_failure && cpufreq_clamp) | ||
663 | wf_control_set_min(cpufreq_clamp); | ||
664 | |||
665 | /* That's it for now, we might want to deal with other failures | ||
666 | * differently in the future though | ||
667 | */ | ||
668 | } | ||
669 | |||
670 | static void pm72_new_control(struct wf_control *ct) | ||
671 | { | ||
672 | bool all_controls; | ||
673 | bool had_pump = cpu_pumps[0] || cpu_pumps[1]; | ||
674 | |||
675 | if (!strcmp(ct->name, "cpu-front-fan-0")) | ||
676 | cpu_front_fans[0] = ct; | ||
677 | else if (!strcmp(ct->name, "cpu-front-fan-1")) | ||
678 | cpu_front_fans[1] = ct; | ||
679 | else if (!strcmp(ct->name, "cpu-rear-fan-0")) | ||
680 | cpu_rear_fans[0] = ct; | ||
681 | else if (!strcmp(ct->name, "cpu-rear-fan-1")) | ||
682 | cpu_rear_fans[1] = ct; | ||
683 | else if (!strcmp(ct->name, "cpu-pump-0")) | ||
684 | cpu_pumps[0] = ct; | ||
685 | else if (!strcmp(ct->name, "cpu-pump-1")) | ||
686 | cpu_pumps[1] = ct; | ||
687 | else if (!strcmp(ct->name, "backside-fan")) | ||
688 | backside_fan = ct; | ||
689 | else if (!strcmp(ct->name, "slots-fan")) | ||
690 | slots_fan = ct; | ||
691 | else if (!strcmp(ct->name, "drive-bay-fan")) | ||
692 | drives_fan = ct; | ||
693 | else if (!strcmp(ct->name, "cpufreq-clamp")) | ||
694 | cpufreq_clamp = ct; | ||
695 | |||
696 | all_controls = | ||
697 | cpu_front_fans[0] && | ||
698 | cpu_rear_fans[0] && | ||
699 | backside_fan && | ||
700 | slots_fan && | ||
701 | drives_fan; | ||
702 | if (nr_chips > 1) | ||
703 | all_controls &= | ||
704 | cpu_front_fans[1] && | ||
705 | cpu_rear_fans[1]; | ||
706 | have_all_controls = all_controls; | ||
707 | |||
708 | if ((cpu_pumps[0] || cpu_pumps[1]) && !had_pump) { | ||
709 | pr_info("wf_pm72: Liquid cooling pump(s) detected," | ||
710 | " using new algorithm !\n"); | ||
711 | cpu_pid_combined = true; | ||
712 | } | ||
713 | } | ||
714 | |||
715 | |||
716 | static void pm72_new_sensor(struct wf_sensor *sr) | ||
717 | { | ||
718 | bool all_sensors; | ||
719 | |||
720 | if (!strcmp(sr->name, "cpu-diode-temp-0")) | ||
721 | sens_cpu_temp[0] = sr; | ||
722 | else if (!strcmp(sr->name, "cpu-diode-temp-1")) | ||
723 | sens_cpu_temp[1] = sr; | ||
724 | else if (!strcmp(sr->name, "cpu-voltage-0")) | ||
725 | sens_cpu_volts[0] = sr; | ||
726 | else if (!strcmp(sr->name, "cpu-voltage-1")) | ||
727 | sens_cpu_volts[1] = sr; | ||
728 | else if (!strcmp(sr->name, "cpu-current-0")) | ||
729 | sens_cpu_amps[0] = sr; | ||
730 | else if (!strcmp(sr->name, "cpu-current-1")) | ||
731 | sens_cpu_amps[1] = sr; | ||
732 | else if (!strcmp(sr->name, "backside-temp")) | ||
733 | backside_temp = sr; | ||
734 | else if (!strcmp(sr->name, "hd-temp")) | ||
735 | drives_temp = sr; | ||
736 | |||
737 | all_sensors = | ||
738 | sens_cpu_temp[0] && | ||
739 | sens_cpu_volts[0] && | ||
740 | sens_cpu_amps[0] && | ||
741 | backside_temp && | ||
742 | drives_temp; | ||
743 | if (nr_chips > 1) | ||
744 | all_sensors &= | ||
745 | sens_cpu_temp[1] && | ||
746 | sens_cpu_volts[1] && | ||
747 | sens_cpu_amps[1]; | ||
748 | |||
749 | have_all_sensors = all_sensors; | ||
750 | } | ||
751 | |||
752 | static int pm72_wf_notify(struct notifier_block *self, | ||
753 | unsigned long event, void *data) | ||
754 | { | ||
755 | switch (event) { | ||
756 | case WF_EVENT_NEW_SENSOR: | ||
757 | pm72_new_sensor(data); | ||
758 | break; | ||
759 | case WF_EVENT_NEW_CONTROL: | ||
760 | pm72_new_control(data); | ||
761 | break; | ||
762 | case WF_EVENT_TICK: | ||
763 | if (have_all_controls && have_all_sensors) | ||
764 | pm72_tick(); | ||
765 | } | ||
766 | return 0; | ||
767 | } | ||
768 | |||
769 | static struct notifier_block pm72_events = { | ||
770 | .notifier_call = pm72_wf_notify, | ||
771 | }; | ||
772 | |||
773 | static int wf_pm72_probe(struct platform_device *dev) | ||
774 | { | ||
775 | wf_register_client(&pm72_events); | ||
776 | return 0; | ||
777 | } | ||
778 | |||
779 | static int wf_pm72_remove(struct platform_device *dev) | ||
780 | { | ||
781 | wf_unregister_client(&pm72_events); | ||
782 | |||
783 | /* should release all sensors and controls */ | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static struct platform_driver wf_pm72_driver = { | ||
788 | .probe = wf_pm72_probe, | ||
789 | .remove = wf_pm72_remove, | ||
790 | .driver = { | ||
791 | .name = "windfarm", | ||
792 | .owner = THIS_MODULE, | ||
793 | }, | ||
794 | }; | ||
795 | |||
796 | static int __init wf_pm72_init(void) | ||
797 | { | ||
798 | struct device_node *cpu; | ||
799 | int i; | ||
800 | |||
801 | if (!of_machine_is_compatible("PowerMac7,2") && | ||
802 | !of_machine_is_compatible("PowerMac7,3")) | ||
803 | return -ENODEV; | ||
804 | |||
805 | /* Count the number of CPU cores */ | ||
806 | nr_chips = 0; | ||
807 | for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) | ||
808 | ++nr_chips; | ||
809 | if (nr_chips > NR_CHIPS) | ||
810 | nr_chips = NR_CHIPS; | ||
811 | |||
812 | pr_info("windfarm: Initializing for desktop G5 with %d chips\n", | ||
813 | nr_chips); | ||
814 | |||
815 | /* Get MPU data for each CPU */ | ||
816 | for (i = 0; i < nr_chips; i++) { | ||
817 | cpu_mpu_data[i] = wf_get_mpu(i); | ||
818 | if (!cpu_mpu_data[i]) { | ||
819 | pr_err("wf_pm72: Failed to find MPU data for CPU %d\n", i); | ||
820 | return -ENXIO; | ||
821 | } | ||
822 | } | ||
823 | |||
824 | #ifdef MODULE | ||
825 | request_module("windfarm_fcu_controls"); | ||
826 | request_module("windfarm_lm75_sensor"); | ||
827 | request_module("windfarm_ad7417_sensor"); | ||
828 | request_module("windfarm_max6690_sensor"); | ||
829 | request_module("windfarm_cpufreq_clamp"); | ||
830 | #endif /* MODULE */ | ||
831 | |||
832 | platform_driver_register(&wf_pm72_driver); | ||
833 | return 0; | ||
834 | } | ||
835 | |||
836 | static void __exit wf_pm72_exit(void) | ||
837 | { | ||
838 | platform_driver_unregister(&wf_pm72_driver); | ||
839 | } | ||
840 | |||
841 | module_init(wf_pm72_init); | ||
842 | module_exit(wf_pm72_exit); | ||
843 | |||
844 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
845 | MODULE_DESCRIPTION("Thermal control for AGP PowerMac G5s"); | ||
846 | MODULE_LICENSE("GPL"); | ||
847 | MODULE_ALIAS("platform:windfarm"); | ||
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index f84933ff329..749d174b0dc 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c | |||
@@ -107,6 +107,7 @@ | |||
107 | #include <asm/prom.h> | 107 | #include <asm/prom.h> |
108 | #include <asm/machdep.h> | 108 | #include <asm/machdep.h> |
109 | #include <asm/io.h> | 109 | #include <asm/io.h> |
110 | #include <asm/system.h> | ||
110 | #include <asm/sections.h> | 111 | #include <asm/sections.h> |
111 | #include <asm/smu.h> | 112 | #include <asm/smu.h> |
112 | 113 | ||
@@ -302,13 +303,13 @@ static void wf_smu_create_sys_fans(void) | |||
302 | pid_param.interval = WF_SMU_SYS_FANS_INTERVAL; | 303 | pid_param.interval = WF_SMU_SYS_FANS_INTERVAL; |
303 | pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE; | 304 | pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE; |
304 | pid_param.itarget = param->itarget; | 305 | pid_param.itarget = param->itarget; |
305 | pid_param.min = wf_control_get_min(fan_system); | 306 | pid_param.min = fan_system->ops->get_min(fan_system); |
306 | pid_param.max = wf_control_get_max(fan_system); | 307 | pid_param.max = fan_system->ops->get_max(fan_system); |
307 | if (fan_hd) { | 308 | if (fan_hd) { |
308 | pid_param.min = | 309 | pid_param.min = |
309 | max(pid_param.min, wf_control_get_min(fan_hd)); | 310 | max(pid_param.min,fan_hd->ops->get_min(fan_hd)); |
310 | pid_param.max = | 311 | pid_param.max = |
311 | min(pid_param.max, wf_control_get_max(fan_hd)); | 312 | min(pid_param.max,fan_hd->ops->get_max(fan_hd)); |
312 | } | 313 | } |
313 | wf_pid_init(&wf_smu_sys_fans->pid, &pid_param); | 314 | wf_pid_init(&wf_smu_sys_fans->pid, &pid_param); |
314 | 315 | ||
@@ -337,7 +338,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) | |||
337 | } | 338 | } |
338 | st->ticks = WF_SMU_SYS_FANS_INTERVAL; | 339 | st->ticks = WF_SMU_SYS_FANS_INTERVAL; |
339 | 340 | ||
340 | rc = wf_sensor_get(sensor_hd_temp, &temp); | 341 | rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); |
341 | if (rc) { | 342 | if (rc) { |
342 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", | 343 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", |
343 | rc); | 344 | rc); |
@@ -373,7 +374,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) | |||
373 | st->hd_setpoint = new_setpoint; | 374 | st->hd_setpoint = new_setpoint; |
374 | readjust: | 375 | readjust: |
375 | if (fan_system && wf_smu_failure_state == 0) { | 376 | if (fan_system && wf_smu_failure_state == 0) { |
376 | rc = wf_control_set(fan_system, st->sys_setpoint); | 377 | rc = fan_system->ops->set_value(fan_system, st->sys_setpoint); |
377 | if (rc) { | 378 | if (rc) { |
378 | printk(KERN_WARNING "windfarm: Sys fan error %d\n", | 379 | printk(KERN_WARNING "windfarm: Sys fan error %d\n", |
379 | rc); | 380 | rc); |
@@ -381,7 +382,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) | |||
381 | } | 382 | } |
382 | } | 383 | } |
383 | if (fan_hd && wf_smu_failure_state == 0) { | 384 | if (fan_hd && wf_smu_failure_state == 0) { |
384 | rc = wf_control_set(fan_hd, st->hd_setpoint); | 385 | rc = fan_hd->ops->set_value(fan_hd, st->hd_setpoint); |
385 | if (rc) { | 386 | if (rc) { |
386 | printk(KERN_WARNING "windfarm: HD fan error %d\n", | 387 | printk(KERN_WARNING "windfarm: HD fan error %d\n", |
387 | rc); | 388 | rc); |
@@ -447,8 +448,8 @@ static void wf_smu_create_cpu_fans(void) | |||
447 | pid_param.ttarget = tmax - tdelta; | 448 | pid_param.ttarget = tmax - tdelta; |
448 | pid_param.pmaxadj = maxpow - powadj; | 449 | pid_param.pmaxadj = maxpow - powadj; |
449 | 450 | ||
450 | pid_param.min = wf_control_get_min(fan_cpu_main); | 451 | pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); |
451 | pid_param.max = wf_control_get_max(fan_cpu_main); | 452 | pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); |
452 | 453 | ||
453 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); | 454 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); |
454 | 455 | ||
@@ -481,7 +482,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
481 | } | 482 | } |
482 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; | 483 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; |
483 | 484 | ||
484 | rc = wf_sensor_get(sensor_cpu_temp, &temp); | 485 | rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); |
485 | if (rc) { | 486 | if (rc) { |
486 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", | 487 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", |
487 | rc); | 488 | rc); |
@@ -489,7 +490,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
489 | return; | 490 | return; |
490 | } | 491 | } |
491 | 492 | ||
492 | rc = wf_sensor_get(sensor_cpu_power, &power); | 493 | rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); |
493 | if (rc) { | 494 | if (rc) { |
494 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", | 495 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", |
495 | rc); | 496 | rc); |
@@ -525,7 +526,8 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
525 | st->cpu_setpoint = new_setpoint; | 526 | st->cpu_setpoint = new_setpoint; |
526 | readjust: | 527 | readjust: |
527 | if (fan_cpu_main && wf_smu_failure_state == 0) { | 528 | if (fan_cpu_main && wf_smu_failure_state == 0) { |
528 | rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); | 529 | rc = fan_cpu_main->ops->set_value(fan_cpu_main, |
530 | st->cpu_setpoint); | ||
529 | if (rc) { | 531 | if (rc) { |
530 | printk(KERN_WARNING "windfarm: CPU main fan" | 532 | printk(KERN_WARNING "windfarm: CPU main fan" |
531 | " error %d\n", rc); | 533 | " error %d\n", rc); |
@@ -720,7 +722,7 @@ static int wf_smu_probe(struct platform_device *ddev) | |||
720 | return 0; | 722 | return 0; |
721 | } | 723 | } |
722 | 724 | ||
723 | static int wf_smu_remove(struct platform_device *ddev) | 725 | static int __devexit wf_smu_remove(struct platform_device *ddev) |
724 | { | 726 | { |
725 | wf_unregister_client(&wf_smu_events); | 727 | wf_unregister_client(&wf_smu_events); |
726 | 728 | ||
@@ -763,7 +765,7 @@ static int wf_smu_remove(struct platform_device *ddev) | |||
763 | 765 | ||
764 | static struct platform_driver wf_smu_driver = { | 766 | static struct platform_driver wf_smu_driver = { |
765 | .probe = wf_smu_probe, | 767 | .probe = wf_smu_probe, |
766 | .remove = wf_smu_remove, | 768 | .remove = __devexit_p(wf_smu_remove), |
767 | .driver = { | 769 | .driver = { |
768 | .name = "windfarm", | 770 | .name = "windfarm", |
769 | .owner = THIS_MODULE, | 771 | .owner = THIS_MODULE, |
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index 2eb484f213c..34427323512 100644 --- a/drivers/macintosh/windfarm_pm91.c +++ b/drivers/macintosh/windfarm_pm91.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <asm/prom.h> | 41 | #include <asm/prom.h> |
42 | #include <asm/machdep.h> | 42 | #include <asm/machdep.h> |
43 | #include <asm/io.h> | 43 | #include <asm/io.h> |
44 | #include <asm/system.h> | ||
44 | #include <asm/sections.h> | 45 | #include <asm/sections.h> |
45 | #include <asm/smu.h> | 46 | #include <asm/smu.h> |
46 | 47 | ||
@@ -192,8 +193,8 @@ static void wf_smu_create_cpu_fans(void) | |||
192 | pid_param.ttarget = tmax - tdelta; | 193 | pid_param.ttarget = tmax - tdelta; |
193 | pid_param.pmaxadj = maxpow - powadj; | 194 | pid_param.pmaxadj = maxpow - powadj; |
194 | 195 | ||
195 | pid_param.min = wf_control_get_min(fan_cpu_main); | 196 | pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); |
196 | pid_param.max = wf_control_get_max(fan_cpu_main); | 197 | pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); |
197 | 198 | ||
198 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); | 199 | wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); |
199 | 200 | ||
@@ -226,7 +227,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
226 | } | 227 | } |
227 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; | 228 | st->ticks = WF_SMU_CPU_FANS_INTERVAL; |
228 | 229 | ||
229 | rc = wf_sensor_get(sensor_cpu_temp, &temp); | 230 | rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); |
230 | if (rc) { | 231 | if (rc) { |
231 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", | 232 | printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", |
232 | rc); | 233 | rc); |
@@ -234,7 +235,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
234 | return; | 235 | return; |
235 | } | 236 | } |
236 | 237 | ||
237 | rc = wf_sensor_get(sensor_cpu_power, &power); | 238 | rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); |
238 | if (rc) { | 239 | if (rc) { |
239 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", | 240 | printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", |
240 | rc); | 241 | rc); |
@@ -261,7 +262,8 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
261 | st->cpu_setpoint = new_setpoint; | 262 | st->cpu_setpoint = new_setpoint; |
262 | readjust: | 263 | readjust: |
263 | if (fan_cpu_main && wf_smu_failure_state == 0) { | 264 | if (fan_cpu_main && wf_smu_failure_state == 0) { |
264 | rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); | 265 | rc = fan_cpu_main->ops->set_value(fan_cpu_main, |
266 | st->cpu_setpoint); | ||
265 | if (rc) { | 267 | if (rc) { |
266 | printk(KERN_WARNING "windfarm: CPU main fan" | 268 | printk(KERN_WARNING "windfarm: CPU main fan" |
267 | " error %d\n", rc); | 269 | " error %d\n", rc); |
@@ -269,7 +271,8 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
269 | } | 271 | } |
270 | } | 272 | } |
271 | if (fan_cpu_second && wf_smu_failure_state == 0) { | 273 | if (fan_cpu_second && wf_smu_failure_state == 0) { |
272 | rc = wf_control_set(fan_cpu_second, st->cpu_setpoint); | 274 | rc = fan_cpu_second->ops->set_value(fan_cpu_second, |
275 | st->cpu_setpoint); | ||
273 | if (rc) { | 276 | if (rc) { |
274 | printk(KERN_WARNING "windfarm: CPU second fan" | 277 | printk(KERN_WARNING "windfarm: CPU second fan" |
275 | " error %d\n", rc); | 278 | " error %d\n", rc); |
@@ -277,7 +280,8 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
277 | } | 280 | } |
278 | } | 281 | } |
279 | if (fan_cpu_third && wf_smu_failure_state == 0) { | 282 | if (fan_cpu_third && wf_smu_failure_state == 0) { |
280 | rc = wf_control_set(fan_cpu_third, st->cpu_setpoint); | 283 | rc = fan_cpu_main->ops->set_value(fan_cpu_third, |
284 | st->cpu_setpoint); | ||
281 | if (rc) { | 285 | if (rc) { |
282 | printk(KERN_WARNING "windfarm: CPU third fan" | 286 | printk(KERN_WARNING "windfarm: CPU third fan" |
283 | " error %d\n", rc); | 287 | " error %d\n", rc); |
@@ -309,8 +313,8 @@ static void wf_smu_create_drive_fans(void) | |||
309 | 313 | ||
310 | /* Fill PID params */ | 314 | /* Fill PID params */ |
311 | param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN); | 315 | param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN); |
312 | param.min = wf_control_get_min(fan_hd); | 316 | param.min = fan_hd->ops->get_min(fan_hd); |
313 | param.max = wf_control_get_max(fan_hd); | 317 | param.max = fan_hd->ops->get_max(fan_hd); |
314 | wf_pid_init(&wf_smu_drive_fans->pid, ¶m); | 318 | wf_pid_init(&wf_smu_drive_fans->pid, ¶m); |
315 | 319 | ||
316 | DBG("wf: Drive Fan control initialized.\n"); | 320 | DBG("wf: Drive Fan control initialized.\n"); |
@@ -335,7 +339,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st) | |||
335 | } | 339 | } |
336 | st->ticks = st->pid.param.interval; | 340 | st->ticks = st->pid.param.interval; |
337 | 341 | ||
338 | rc = wf_sensor_get(sensor_hd_temp, &temp); | 342 | rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); |
339 | if (rc) { | 343 | if (rc) { |
340 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", | 344 | printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", |
341 | rc); | 345 | rc); |
@@ -358,7 +362,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st) | |||
358 | st->setpoint = new_setpoint; | 362 | st->setpoint = new_setpoint; |
359 | readjust: | 363 | readjust: |
360 | if (fan_hd && wf_smu_failure_state == 0) { | 364 | if (fan_hd && wf_smu_failure_state == 0) { |
361 | rc = wf_control_set(fan_hd, st->setpoint); | 365 | rc = fan_hd->ops->set_value(fan_hd, st->setpoint); |
362 | if (rc) { | 366 | if (rc) { |
363 | printk(KERN_WARNING "windfarm: HD fan error %d\n", | 367 | printk(KERN_WARNING "windfarm: HD fan error %d\n", |
364 | rc); | 368 | rc); |
@@ -390,8 +394,8 @@ static void wf_smu_create_slots_fans(void) | |||
390 | 394 | ||
391 | /* Fill PID params */ | 395 | /* Fill PID params */ |
392 | param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN); | 396 | param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN); |
393 | param.min = wf_control_get_min(fan_slots); | 397 | param.min = fan_slots->ops->get_min(fan_slots); |
394 | param.max = wf_control_get_max(fan_slots); | 398 | param.max = fan_slots->ops->get_max(fan_slots); |
395 | wf_pid_init(&wf_smu_slots_fans->pid, ¶m); | 399 | wf_pid_init(&wf_smu_slots_fans->pid, ¶m); |
396 | 400 | ||
397 | DBG("wf: Slots Fan control initialized.\n"); | 401 | DBG("wf: Slots Fan control initialized.\n"); |
@@ -416,7 +420,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) | |||
416 | } | 420 | } |
417 | st->ticks = st->pid.param.interval; | 421 | st->ticks = st->pid.param.interval; |
418 | 422 | ||
419 | rc = wf_sensor_get(sensor_slots_power, &power); | 423 | rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power); |
420 | if (rc) { | 424 | if (rc) { |
421 | printk(KERN_WARNING "windfarm: Slots power sensor error %d\n", | 425 | printk(KERN_WARNING "windfarm: Slots power sensor error %d\n", |
422 | rc); | 426 | rc); |
@@ -441,7 +445,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) | |||
441 | st->setpoint = new_setpoint; | 445 | st->setpoint = new_setpoint; |
442 | readjust: | 446 | readjust: |
443 | if (fan_slots && wf_smu_failure_state == 0) { | 447 | if (fan_slots && wf_smu_failure_state == 0) { |
444 | rc = wf_control_set(fan_slots, st->setpoint); | 448 | rc = fan_slots->ops->set_value(fan_slots, st->setpoint); |
445 | if (rc) { | 449 | if (rc) { |
446 | printk(KERN_WARNING "windfarm: Slots fan error %d\n", | 450 | printk(KERN_WARNING "windfarm: Slots fan error %d\n", |
447 | rc); | 451 | rc); |
@@ -642,7 +646,7 @@ static int wf_smu_probe(struct platform_device *ddev) | |||
642 | return 0; | 646 | return 0; |
643 | } | 647 | } |
644 | 648 | ||
645 | static int wf_smu_remove(struct platform_device *ddev) | 649 | static int __devexit wf_smu_remove(struct platform_device *ddev) |
646 | { | 650 | { |
647 | wf_unregister_client(&wf_smu_events); | 651 | wf_unregister_client(&wf_smu_events); |
648 | 652 | ||
@@ -692,7 +696,7 @@ static int wf_smu_remove(struct platform_device *ddev) | |||
692 | 696 | ||
693 | static struct platform_driver wf_smu_driver = { | 697 | static struct platform_driver wf_smu_driver = { |
694 | .probe = wf_smu_probe, | 698 | .probe = wf_smu_probe, |
695 | .remove = wf_smu_remove, | 699 | .remove = __devexit_p(wf_smu_remove), |
696 | .driver = { | 700 | .driver = { |
697 | .name = "windfarm", | 701 | .name = "windfarm", |
698 | .owner = THIS_MODULE, | 702 | .owner = THIS_MODULE, |
diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c deleted file mode 100644 index 844003fb4ef..00000000000 --- a/drivers/macintosh/windfarm_rm31.c +++ /dev/null | |||
@@ -1,740 +0,0 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. | ||
3 | * Control loops for RackMack3,1 (Xserve G5) | ||
4 | * | ||
5 | * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. | ||
6 | * | ||
7 | * Use and redistribute under the terms of the GNU GPL v2. | ||
8 | */ | ||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/device.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/reboot.h> | ||
15 | #include <asm/prom.h> | ||
16 | #include <asm/smu.h> | ||
17 | |||
18 | #include "windfarm.h" | ||
19 | #include "windfarm_pid.h" | ||
20 | #include "windfarm_mpu.h" | ||
21 | |||
22 | #define VERSION "1.0" | ||
23 | |||
24 | #undef DEBUG | ||
25 | #undef LOTSA_DEBUG | ||
26 | |||
27 | #ifdef DEBUG | ||
28 | #define DBG(args...) printk(args) | ||
29 | #else | ||
30 | #define DBG(args...) do { } while(0) | ||
31 | #endif | ||
32 | |||
33 | #ifdef LOTSA_DEBUG | ||
34 | #define DBG_LOTS(args...) printk(args) | ||
35 | #else | ||
36 | #define DBG_LOTS(args...) do { } while(0) | ||
37 | #endif | ||
38 | |||
39 | /* define this to force CPU overtemp to 60 degree, useful for testing | ||
40 | * the overtemp code | ||
41 | */ | ||
42 | #undef HACKED_OVERTEMP | ||
43 | |||
44 | /* We currently only handle 2 chips */ | ||
45 | #define NR_CHIPS 2 | ||
46 | #define NR_CPU_FANS 3 * NR_CHIPS | ||
47 | |||
48 | /* Controls and sensors */ | ||
49 | static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; | ||
50 | static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; | ||
51 | static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; | ||
52 | static struct wf_sensor *backside_temp; | ||
53 | static struct wf_sensor *slots_temp; | ||
54 | static struct wf_sensor *dimms_temp; | ||
55 | |||
56 | static struct wf_control *cpu_fans[NR_CHIPS][3]; | ||
57 | static struct wf_control *backside_fan; | ||
58 | static struct wf_control *slots_fan; | ||
59 | static struct wf_control *cpufreq_clamp; | ||
60 | |||
61 | /* We keep a temperature history for average calculation of 180s */ | ||
62 | #define CPU_TEMP_HIST_SIZE 180 | ||
63 | |||
64 | /* PID loop state */ | ||
65 | static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; | ||
66 | static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; | ||
67 | static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; | ||
68 | static int cpu_thist_pt; | ||
69 | static s64 cpu_thist_total; | ||
70 | static s32 cpu_all_tmax = 100 << 16; | ||
71 | static struct wf_pid_state backside_pid; | ||
72 | static int backside_tick; | ||
73 | static struct wf_pid_state slots_pid; | ||
74 | static int slots_tick; | ||
75 | static int slots_speed; | ||
76 | static struct wf_pid_state dimms_pid; | ||
77 | static int dimms_output_clamp; | ||
78 | |||
79 | static int nr_chips; | ||
80 | static bool have_all_controls; | ||
81 | static bool have_all_sensors; | ||
82 | static bool started; | ||
83 | |||
84 | static int failure_state; | ||
85 | #define FAILURE_SENSOR 1 | ||
86 | #define FAILURE_FAN 2 | ||
87 | #define FAILURE_PERM 4 | ||
88 | #define FAILURE_LOW_OVERTEMP 8 | ||
89 | #define FAILURE_HIGH_OVERTEMP 16 | ||
90 | |||
91 | /* Overtemp values */ | ||
92 | #define LOW_OVER_AVERAGE 0 | ||
93 | #define LOW_OVER_IMMEDIATE (10 << 16) | ||
94 | #define LOW_OVER_CLEAR ((-10) << 16) | ||
95 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
96 | #define HIGH_OVER_AVERAGE (10 << 16) | ||
97 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
98 | |||
99 | |||
100 | static void cpu_max_all_fans(void) | ||
101 | { | ||
102 | int i; | ||
103 | |||
104 | /* We max all CPU fans in case of a sensor error. We also do the | ||
105 | * cpufreq clamping now, even if it's supposedly done later by the | ||
106 | * generic code anyway, we do it earlier here to react faster | ||
107 | */ | ||
108 | if (cpufreq_clamp) | ||
109 | wf_control_set_max(cpufreq_clamp); | ||
110 | for (i = 0; i < nr_chips; i++) { | ||
111 | if (cpu_fans[i][0]) | ||
112 | wf_control_set_max(cpu_fans[i][0]); | ||
113 | if (cpu_fans[i][1]) | ||
114 | wf_control_set_max(cpu_fans[i][1]); | ||
115 | if (cpu_fans[i][2]) | ||
116 | wf_control_set_max(cpu_fans[i][2]); | ||
117 | } | ||
118 | } | ||
119 | |||
120 | static int cpu_check_overtemp(s32 temp) | ||
121 | { | ||
122 | int new_state = 0; | ||
123 | s32 t_avg, t_old; | ||
124 | static bool first = true; | ||
125 | |||
126 | /* First check for immediate overtemps */ | ||
127 | if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { | ||
128 | new_state |= FAILURE_LOW_OVERTEMP; | ||
129 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
130 | printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" | ||
131 | " temperature !\n"); | ||
132 | } | ||
133 | if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { | ||
134 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
135 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
136 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
137 | " immediate CPU temperature !\n"); | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * The first time around, initialize the array with the first | ||
142 | * temperature reading | ||
143 | */ | ||
144 | if (first) { | ||
145 | int i; | ||
146 | |||
147 | cpu_thist_total = 0; | ||
148 | for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { | ||
149 | cpu_thist[i] = temp; | ||
150 | cpu_thist_total += temp; | ||
151 | } | ||
152 | first = false; | ||
153 | } | ||
154 | |||
155 | /* | ||
156 | * We calculate a history of max temperatures and use that for the | ||
157 | * overtemp management | ||
158 | */ | ||
159 | t_old = cpu_thist[cpu_thist_pt]; | ||
160 | cpu_thist[cpu_thist_pt] = temp; | ||
161 | cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; | ||
162 | cpu_thist_total -= t_old; | ||
163 | cpu_thist_total += temp; | ||
164 | t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; | ||
165 | |||
166 | DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", | ||
167 | FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); | ||
168 | |||
169 | /* Now check for average overtemps */ | ||
170 | if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { | ||
171 | new_state |= FAILURE_LOW_OVERTEMP; | ||
172 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
173 | printk(KERN_ERR "windfarm: Overtemp due to average CPU" | ||
174 | " temperature !\n"); | ||
175 | } | ||
176 | if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { | ||
177 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
178 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
179 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
180 | " average CPU temperature !\n"); | ||
181 | } | ||
182 | |||
183 | /* Now handle overtemp conditions. We don't currently use the windfarm | ||
184 | * overtemp handling core as it's not fully suited to the needs of those | ||
185 | * new machine. This will be fixed later. | ||
186 | */ | ||
187 | if (new_state) { | ||
188 | /* High overtemp -> immediate shutdown */ | ||
189 | if (new_state & FAILURE_HIGH_OVERTEMP) | ||
190 | machine_power_off(); | ||
191 | if ((failure_state & new_state) != new_state) | ||
192 | cpu_max_all_fans(); | ||
193 | failure_state |= new_state; | ||
194 | } else if ((failure_state & FAILURE_LOW_OVERTEMP) && | ||
195 | (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { | ||
196 | printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); | ||
197 | failure_state &= ~FAILURE_LOW_OVERTEMP; | ||
198 | } | ||
199 | |||
200 | return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); | ||
201 | } | ||
202 | |||
203 | static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) | ||
204 | { | ||
205 | s32 dtemp, volts, amps; | ||
206 | int rc; | ||
207 | |||
208 | /* Get diode temperature */ | ||
209 | rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); | ||
210 | if (rc) { | ||
211 | DBG(" CPU%d: temp reading error !\n", cpu); | ||
212 | return -EIO; | ||
213 | } | ||
214 | DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); | ||
215 | *temp = dtemp; | ||
216 | |||
217 | /* Get voltage */ | ||
218 | rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); | ||
219 | if (rc) { | ||
220 | DBG(" CPU%d, volts reading error !\n", cpu); | ||
221 | return -EIO; | ||
222 | } | ||
223 | DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); | ||
224 | |||
225 | /* Get current */ | ||
226 | rc = wf_sensor_get(sens_cpu_amps[cpu], &s); | ||
227 | if (rc) { | ||
228 | DBG(" CPU%d, current reading error !\n", cpu); | ||
229 | return -EIO; | ||
230 | } | ||
231 | DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); | ||
232 | |||
233 | /* Calculate power */ | ||
234 | |||
235 | /* Scale voltage and current raw sensor values according to fixed scales | ||
236 | * obtained in Darwin and calculate power from I and V | ||
237 | */ | ||
238 | *power = (((u64)volts) * ((u64)amps)) >> 16; | ||
239 | |||
240 | DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); | ||
241 | |||
242 | return 0; | ||
243 | |||
244 | } | ||
245 | |||
246 | static void cpu_fans_tick(void) | ||
247 | { | ||
248 | int err, cpu, i; | ||
249 | s32 speed, temp, power, t_max = 0; | ||
250 | |||
251 | DBG_LOTS("* cpu fans_tick_split()\n"); | ||
252 | |||
253 | for (cpu = 0; cpu < nr_chips; ++cpu) { | ||
254 | struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; | ||
255 | |||
256 | /* Read current speed */ | ||
257 | wf_control_get(cpu_fans[cpu][0], &sp->target); | ||
258 | |||
259 | err = read_one_cpu_vals(cpu, &temp, &power); | ||
260 | if (err) { | ||
261 | failure_state |= FAILURE_SENSOR; | ||
262 | cpu_max_all_fans(); | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | /* Keep track of highest temp */ | ||
267 | t_max = max(t_max, temp); | ||
268 | |||
269 | /* Handle possible overtemps */ | ||
270 | if (cpu_check_overtemp(t_max)) | ||
271 | return; | ||
272 | |||
273 | /* Run PID */ | ||
274 | wf_cpu_pid_run(sp, power, temp); | ||
275 | |||
276 | DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); | ||
277 | |||
278 | /* Apply DIMMs clamp */ | ||
279 | speed = max(sp->target, dimms_output_clamp); | ||
280 | |||
281 | /* Apply result to all cpu fans */ | ||
282 | for (i = 0; i < 3; i++) { | ||
283 | err = wf_control_set(cpu_fans[cpu][i], speed); | ||
284 | if (err) { | ||
285 | pr_warning("wf_rm31: Fan %s reports error %d\n", | ||
286 | cpu_fans[cpu][i]->name, err); | ||
287 | failure_state |= FAILURE_FAN; | ||
288 | } | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | /* Implementation... */ | ||
294 | static int cpu_setup_pid(int cpu) | ||
295 | { | ||
296 | struct wf_cpu_pid_param pid; | ||
297 | const struct mpu_data *mpu = cpu_mpu_data[cpu]; | ||
298 | s32 tmax, ttarget, ptarget; | ||
299 | int fmin, fmax, hsize; | ||
300 | |||
301 | /* Get PID params from the appropriate MPU EEPROM */ | ||
302 | tmax = mpu->tmax << 16; | ||
303 | ttarget = mpu->ttarget << 16; | ||
304 | ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; | ||
305 | |||
306 | DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", | ||
307 | cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); | ||
308 | |||
309 | /* We keep a global tmax for overtemp calculations */ | ||
310 | if (tmax < cpu_all_tmax) | ||
311 | cpu_all_tmax = tmax; | ||
312 | |||
313 | /* Set PID min/max by using the rear fan min/max */ | ||
314 | fmin = wf_control_get_min(cpu_fans[cpu][0]); | ||
315 | fmax = wf_control_get_max(cpu_fans[cpu][0]); | ||
316 | DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); | ||
317 | |||
318 | /* History size */ | ||
319 | hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); | ||
320 | DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); | ||
321 | |||
322 | /* Initialize PID loop */ | ||
323 | pid.interval = 1; /* seconds */ | ||
324 | pid.history_len = hsize; | ||
325 | pid.gd = mpu->pid_gd; | ||
326 | pid.gp = mpu->pid_gp; | ||
327 | pid.gr = mpu->pid_gr; | ||
328 | pid.tmax = tmax; | ||
329 | pid.ttarget = ttarget; | ||
330 | pid.pmaxadj = ptarget; | ||
331 | pid.min = fmin; | ||
332 | pid.max = fmax; | ||
333 | |||
334 | wf_cpu_pid_init(&cpu_pid[cpu], &pid); | ||
335 | cpu_pid[cpu].target = 4000; | ||
336 | |||
337 | return 0; | ||
338 | } | ||
339 | |||
340 | /* Backside/U3 fan */ | ||
341 | static struct wf_pid_param backside_param = { | ||
342 | .interval = 1, | ||
343 | .history_len = 2, | ||
344 | .gd = 0x00500000, | ||
345 | .gp = 0x0004cccc, | ||
346 | .gr = 0, | ||
347 | .itarget = 70 << 16, | ||
348 | .additive = 0, | ||
349 | .min = 20, | ||
350 | .max = 100, | ||
351 | }; | ||
352 | |||
353 | /* DIMMs temperature (clamp the backside fan) */ | ||
354 | static struct wf_pid_param dimms_param = { | ||
355 | .interval = 1, | ||
356 | .history_len = 20, | ||
357 | .gd = 0, | ||
358 | .gp = 0, | ||
359 | .gr = 0x06553600, | ||
360 | .itarget = 50 << 16, | ||
361 | .additive = 0, | ||
362 | .min = 4000, | ||
363 | .max = 14000, | ||
364 | }; | ||
365 | |||
366 | static void backside_fan_tick(void) | ||
367 | { | ||
368 | s32 temp, dtemp; | ||
369 | int speed, dspeed, fan_min; | ||
370 | int err; | ||
371 | |||
372 | if (!backside_fan || !backside_temp || !dimms_temp || !backside_tick) | ||
373 | return; | ||
374 | if (--backside_tick > 0) | ||
375 | return; | ||
376 | backside_tick = backside_pid.param.interval; | ||
377 | |||
378 | DBG_LOTS("* backside fans tick\n"); | ||
379 | |||
380 | /* Update fan speed from actual fans */ | ||
381 | err = wf_control_get(backside_fan, &speed); | ||
382 | if (!err) | ||
383 | backside_pid.target = speed; | ||
384 | |||
385 | err = wf_sensor_get(backside_temp, &temp); | ||
386 | if (err) { | ||
387 | printk(KERN_WARNING "windfarm: U3 temp sensor error %d\n", | ||
388 | err); | ||
389 | failure_state |= FAILURE_SENSOR; | ||
390 | wf_control_set_max(backside_fan); | ||
391 | return; | ||
392 | } | ||
393 | speed = wf_pid_run(&backside_pid, temp); | ||
394 | |||
395 | DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", | ||
396 | FIX32TOPRINT(temp), speed); | ||
397 | |||
398 | err = wf_sensor_get(dimms_temp, &dtemp); | ||
399 | if (err) { | ||
400 | printk(KERN_WARNING "windfarm: DIMMs temp sensor error %d\n", | ||
401 | err); | ||
402 | failure_state |= FAILURE_SENSOR; | ||
403 | wf_control_set_max(backside_fan); | ||
404 | return; | ||
405 | } | ||
406 | dspeed = wf_pid_run(&dimms_pid, dtemp); | ||
407 | dimms_output_clamp = dspeed; | ||
408 | |||
409 | fan_min = (dspeed * 100) / 14000; | ||
410 | fan_min = max(fan_min, backside_param.min); | ||
411 | speed = max(speed, fan_min); | ||
412 | |||
413 | err = wf_control_set(backside_fan, speed); | ||
414 | if (err) { | ||
415 | printk(KERN_WARNING "windfarm: backside fan error %d\n", err); | ||
416 | failure_state |= FAILURE_FAN; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | static void backside_setup_pid(void) | ||
421 | { | ||
422 | /* first time initialize things */ | ||
423 | s32 fmin = wf_control_get_min(backside_fan); | ||
424 | s32 fmax = wf_control_get_max(backside_fan); | ||
425 | struct wf_pid_param param; | ||
426 | |||
427 | param = backside_param; | ||
428 | param.min = max(param.min, fmin); | ||
429 | param.max = min(param.max, fmax); | ||
430 | wf_pid_init(&backside_pid, ¶m); | ||
431 | |||
432 | param = dimms_param; | ||
433 | wf_pid_init(&dimms_pid, ¶m); | ||
434 | |||
435 | backside_tick = 1; | ||
436 | |||
437 | pr_info("wf_rm31: Backside control loop started.\n"); | ||
438 | } | ||
439 | |||
440 | /* Slots fan */ | ||
441 | static const struct wf_pid_param slots_param = { | ||
442 | .interval = 5, | ||
443 | .history_len = 2, | ||
444 | .gd = 30 << 20, | ||
445 | .gp = 5 << 20, | ||
446 | .gr = 0, | ||
447 | .itarget = 40 << 16, | ||
448 | .additive = 1, | ||
449 | .min = 300, | ||
450 | .max = 4000, | ||
451 | }; | ||
452 | |||
453 | static void slots_fan_tick(void) | ||
454 | { | ||
455 | s32 temp; | ||
456 | int speed; | ||
457 | int err; | ||
458 | |||
459 | if (!slots_fan || !slots_temp || !slots_tick) | ||
460 | return; | ||
461 | if (--slots_tick > 0) | ||
462 | return; | ||
463 | slots_tick = slots_pid.param.interval; | ||
464 | |||
465 | DBG_LOTS("* slots fans tick\n"); | ||
466 | |||
467 | err = wf_sensor_get(slots_temp, &temp); | ||
468 | if (err) { | ||
469 | pr_warning("wf_rm31: slots temp sensor error %d\n", err); | ||
470 | failure_state |= FAILURE_SENSOR; | ||
471 | wf_control_set_max(slots_fan); | ||
472 | return; | ||
473 | } | ||
474 | speed = wf_pid_run(&slots_pid, temp); | ||
475 | |||
476 | DBG_LOTS("slots PID temp=%d.%.3d speed=%d\n", | ||
477 | FIX32TOPRINT(temp), speed); | ||
478 | |||
479 | slots_speed = speed; | ||
480 | err = wf_control_set(slots_fan, speed); | ||
481 | if (err) { | ||
482 | printk(KERN_WARNING "windfarm: slots bay fan error %d\n", err); | ||
483 | failure_state |= FAILURE_FAN; | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void slots_setup_pid(void) | ||
488 | { | ||
489 | /* first time initialize things */ | ||
490 | s32 fmin = wf_control_get_min(slots_fan); | ||
491 | s32 fmax = wf_control_get_max(slots_fan); | ||
492 | struct wf_pid_param param = slots_param; | ||
493 | |||
494 | param.min = max(param.min, fmin); | ||
495 | param.max = min(param.max, fmax); | ||
496 | wf_pid_init(&slots_pid, ¶m); | ||
497 | slots_tick = 1; | ||
498 | |||
499 | pr_info("wf_rm31: Slots control loop started.\n"); | ||
500 | } | ||
501 | |||
502 | static void set_fail_state(void) | ||
503 | { | ||
504 | cpu_max_all_fans(); | ||
505 | |||
506 | if (backside_fan) | ||
507 | wf_control_set_max(backside_fan); | ||
508 | if (slots_fan) | ||
509 | wf_control_set_max(slots_fan); | ||
510 | } | ||
511 | |||
512 | static void rm31_tick(void) | ||
513 | { | ||
514 | int i, last_failure; | ||
515 | |||
516 | if (!started) { | ||
517 | started = 1; | ||
518 | printk(KERN_INFO "windfarm: CPUs control loops started.\n"); | ||
519 | for (i = 0; i < nr_chips; ++i) { | ||
520 | if (cpu_setup_pid(i) < 0) { | ||
521 | failure_state = FAILURE_PERM; | ||
522 | set_fail_state(); | ||
523 | break; | ||
524 | } | ||
525 | } | ||
526 | DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); | ||
527 | |||
528 | backside_setup_pid(); | ||
529 | slots_setup_pid(); | ||
530 | |||
531 | #ifdef HACKED_OVERTEMP | ||
532 | cpu_all_tmax = 60 << 16; | ||
533 | #endif | ||
534 | } | ||
535 | |||
536 | /* Permanent failure, bail out */ | ||
537 | if (failure_state & FAILURE_PERM) | ||
538 | return; | ||
539 | |||
540 | /* | ||
541 | * Clear all failure bits except low overtemp which will be eventually | ||
542 | * cleared by the control loop itself | ||
543 | */ | ||
544 | last_failure = failure_state; | ||
545 | failure_state &= FAILURE_LOW_OVERTEMP; | ||
546 | backside_fan_tick(); | ||
547 | slots_fan_tick(); | ||
548 | |||
549 | /* We do CPUs last because they can be clamped high by | ||
550 | * DIMM temperature | ||
551 | */ | ||
552 | cpu_fans_tick(); | ||
553 | |||
554 | DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", | ||
555 | last_failure, failure_state); | ||
556 | |||
557 | /* Check for failures. Any failure causes cpufreq clamping */ | ||
558 | if (failure_state && last_failure == 0 && cpufreq_clamp) | ||
559 | wf_control_set_max(cpufreq_clamp); | ||
560 | if (failure_state == 0 && last_failure && cpufreq_clamp) | ||
561 | wf_control_set_min(cpufreq_clamp); | ||
562 | |||
563 | /* That's it for now, we might want to deal with other failures | ||
564 | * differently in the future though | ||
565 | */ | ||
566 | } | ||
567 | |||
568 | static void rm31_new_control(struct wf_control *ct) | ||
569 | { | ||
570 | bool all_controls; | ||
571 | |||
572 | if (!strcmp(ct->name, "cpu-fan-a-0")) | ||
573 | cpu_fans[0][0] = ct; | ||
574 | else if (!strcmp(ct->name, "cpu-fan-b-0")) | ||
575 | cpu_fans[0][1] = ct; | ||
576 | else if (!strcmp(ct->name, "cpu-fan-c-0")) | ||
577 | cpu_fans[0][2] = ct; | ||
578 | else if (!strcmp(ct->name, "cpu-fan-a-1")) | ||
579 | cpu_fans[1][0] = ct; | ||
580 | else if (!strcmp(ct->name, "cpu-fan-b-1")) | ||
581 | cpu_fans[1][1] = ct; | ||
582 | else if (!strcmp(ct->name, "cpu-fan-c-1")) | ||
583 | cpu_fans[1][2] = ct; | ||
584 | else if (!strcmp(ct->name, "backside-fan")) | ||
585 | backside_fan = ct; | ||
586 | else if (!strcmp(ct->name, "slots-fan")) | ||
587 | slots_fan = ct; | ||
588 | else if (!strcmp(ct->name, "cpufreq-clamp")) | ||
589 | cpufreq_clamp = ct; | ||
590 | |||
591 | all_controls = | ||
592 | cpu_fans[0][0] && | ||
593 | cpu_fans[0][1] && | ||
594 | cpu_fans[0][2] && | ||
595 | backside_fan && | ||
596 | slots_fan; | ||
597 | if (nr_chips > 1) | ||
598 | all_controls &= | ||
599 | cpu_fans[1][0] && | ||
600 | cpu_fans[1][1] && | ||
601 | cpu_fans[1][2]; | ||
602 | have_all_controls = all_controls; | ||
603 | } | ||
604 | |||
605 | |||
606 | static void rm31_new_sensor(struct wf_sensor *sr) | ||
607 | { | ||
608 | bool all_sensors; | ||
609 | |||
610 | if (!strcmp(sr->name, "cpu-diode-temp-0")) | ||
611 | sens_cpu_temp[0] = sr; | ||
612 | else if (!strcmp(sr->name, "cpu-diode-temp-1")) | ||
613 | sens_cpu_temp[1] = sr; | ||
614 | else if (!strcmp(sr->name, "cpu-voltage-0")) | ||
615 | sens_cpu_volts[0] = sr; | ||
616 | else if (!strcmp(sr->name, "cpu-voltage-1")) | ||
617 | sens_cpu_volts[1] = sr; | ||
618 | else if (!strcmp(sr->name, "cpu-current-0")) | ||
619 | sens_cpu_amps[0] = sr; | ||
620 | else if (!strcmp(sr->name, "cpu-current-1")) | ||
621 | sens_cpu_amps[1] = sr; | ||
622 | else if (!strcmp(sr->name, "backside-temp")) | ||
623 | backside_temp = sr; | ||
624 | else if (!strcmp(sr->name, "slots-temp")) | ||
625 | slots_temp = sr; | ||
626 | else if (!strcmp(sr->name, "dimms-temp")) | ||
627 | dimms_temp = sr; | ||
628 | |||
629 | all_sensors = | ||
630 | sens_cpu_temp[0] && | ||
631 | sens_cpu_volts[0] && | ||
632 | sens_cpu_amps[0] && | ||
633 | backside_temp && | ||
634 | slots_temp && | ||
635 | dimms_temp; | ||
636 | if (nr_chips > 1) | ||
637 | all_sensors &= | ||
638 | sens_cpu_temp[1] && | ||
639 | sens_cpu_volts[1] && | ||
640 | sens_cpu_amps[1]; | ||
641 | |||
642 | have_all_sensors = all_sensors; | ||
643 | } | ||
644 | |||
645 | static int rm31_wf_notify(struct notifier_block *self, | ||
646 | unsigned long event, void *data) | ||
647 | { | ||
648 | switch (event) { | ||
649 | case WF_EVENT_NEW_SENSOR: | ||
650 | rm31_new_sensor(data); | ||
651 | break; | ||
652 | case WF_EVENT_NEW_CONTROL: | ||
653 | rm31_new_control(data); | ||
654 | break; | ||
655 | case WF_EVENT_TICK: | ||
656 | if (have_all_controls && have_all_sensors) | ||
657 | rm31_tick(); | ||
658 | } | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static struct notifier_block rm31_events = { | ||
663 | .notifier_call = rm31_wf_notify, | ||
664 | }; | ||
665 | |||
666 | static int wf_rm31_probe(struct platform_device *dev) | ||
667 | { | ||
668 | wf_register_client(&rm31_events); | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int wf_rm31_remove(struct platform_device *dev) | ||
673 | { | ||
674 | wf_unregister_client(&rm31_events); | ||
675 | |||
676 | /* should release all sensors and controls */ | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | static struct platform_driver wf_rm31_driver = { | ||
681 | .probe = wf_rm31_probe, | ||
682 | .remove = wf_rm31_remove, | ||
683 | .driver = { | ||
684 | .name = "windfarm", | ||
685 | .owner = THIS_MODULE, | ||
686 | }, | ||
687 | }; | ||
688 | |||
689 | static int __init wf_rm31_init(void) | ||
690 | { | ||
691 | struct device_node *cpu; | ||
692 | int i; | ||
693 | |||
694 | if (!of_machine_is_compatible("RackMac3,1")) | ||
695 | return -ENODEV; | ||
696 | |||
697 | /* Count the number of CPU cores */ | ||
698 | nr_chips = 0; | ||
699 | for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) | ||
700 | ++nr_chips; | ||
701 | if (nr_chips > NR_CHIPS) | ||
702 | nr_chips = NR_CHIPS; | ||
703 | |||
704 | pr_info("windfarm: Initializing for desktop G5 with %d chips\n", | ||
705 | nr_chips); | ||
706 | |||
707 | /* Get MPU data for each CPU */ | ||
708 | for (i = 0; i < nr_chips; i++) { | ||
709 | cpu_mpu_data[i] = wf_get_mpu(i); | ||
710 | if (!cpu_mpu_data[i]) { | ||
711 | pr_err("wf_rm31: Failed to find MPU data for CPU %d\n", i); | ||
712 | return -ENXIO; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | #ifdef MODULE | ||
717 | request_module("windfarm_fcu_controls"); | ||
718 | request_module("windfarm_lm75_sensor"); | ||
719 | request_module("windfarm_lm87_sensor"); | ||
720 | request_module("windfarm_ad7417_sensor"); | ||
721 | request_module("windfarm_max6690_sensor"); | ||
722 | request_module("windfarm_cpufreq_clamp"); | ||
723 | #endif /* MODULE */ | ||
724 | |||
725 | platform_driver_register(&wf_rm31_driver); | ||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static void __exit wf_rm31_exit(void) | ||
730 | { | ||
731 | platform_driver_unregister(&wf_rm31_driver); | ||
732 | } | ||
733 | |||
734 | module_init(wf_rm31_init); | ||
735 | module_exit(wf_rm31_exit); | ||
736 | |||
737 | MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); | ||
738 | MODULE_DESCRIPTION("Thermal control for Xserve G5"); | ||
739 | MODULE_LICENSE("GPL"); | ||
740 | MODULE_ALIAS("platform:windfarm"); | ||
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index c155a54e863..43137b421f9 100644 --- a/drivers/macintosh/windfarm_smu_controls.c +++ b/drivers/macintosh/windfarm_smu_controls.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <asm/prom.h> | 18 | #include <asm/prom.h> |
19 | #include <asm/machdep.h> | 19 | #include <asm/machdep.h> |
20 | #include <asm/io.h> | 20 | #include <asm/io.h> |
21 | #include <asm/system.h> | ||
21 | #include <asm/sections.h> | 22 | #include <asm/sections.h> |
22 | #include <asm/smu.h> | 23 | #include <asm/smu.h> |
23 | 24 | ||
@@ -172,6 +173,7 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, | |||
172 | 173 | ||
173 | fct->fan_type = pwm_fan; | 174 | fct->fan_type = pwm_fan; |
174 | fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN; | 175 | fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN; |
176 | sysfs_attr_init(&fct->ctrl.attr.attr); | ||
175 | 177 | ||
176 | /* We use the name & location here the same way we do for SMU sensors, | 178 | /* We use the name & location here the same way we do for SMU sensors, |
177 | * see the comment in windfarm_smu_sensors.c. The locations are a bit | 179 | * see the comment in windfarm_smu_sensors.c. The locations are a bit |
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index d87f5ee04ca..65a8ff3e1f8 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c | |||
@@ -20,7 +20,7 @@ | |||
20 | 20 | ||
21 | #include "windfarm.h" | 21 | #include "windfarm.h" |
22 | 22 | ||
23 | #define VERSION "1.0" | 23 | #define VERSION "0.2" |
24 | 24 | ||
25 | #define DEBUG | 25 | #define DEBUG |
26 | 26 | ||
@@ -34,12 +34,11 @@ | |||
34 | #define MAX_AGE msecs_to_jiffies(800) | 34 | #define MAX_AGE msecs_to_jiffies(800) |
35 | 35 | ||
36 | struct wf_sat { | 36 | struct wf_sat { |
37 | struct kref ref; | ||
38 | int nr; | 37 | int nr; |
38 | atomic_t refcnt; | ||
39 | struct mutex mutex; | 39 | struct mutex mutex; |
40 | unsigned long last_read; /* jiffies when cache last updated */ | 40 | unsigned long last_read; /* jiffies when cache last updated */ |
41 | u8 cache[16]; | 41 | u8 cache[16]; |
42 | struct list_head sensors; | ||
43 | struct i2c_client *i2c; | 42 | struct i2c_client *i2c; |
44 | struct device_node *node; | 43 | struct device_node *node; |
45 | }; | 44 | }; |
@@ -47,12 +46,11 @@ struct wf_sat { | |||
47 | static struct wf_sat *sats[2]; | 46 | static struct wf_sat *sats[2]; |
48 | 47 | ||
49 | struct wf_sat_sensor { | 48 | struct wf_sat_sensor { |
50 | struct list_head link; | 49 | int index; |
51 | int index; | 50 | int index2; /* used for power sensors */ |
52 | int index2; /* used for power sensors */ | 51 | int shift; |
53 | int shift; | 52 | struct wf_sat *sat; |
54 | struct wf_sat *sat; | 53 | struct wf_sensor sens; |
55 | struct wf_sensor sens; | ||
56 | }; | 54 | }; |
57 | 55 | ||
58 | #define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) | 56 | #define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) |
@@ -144,7 +142,7 @@ static int wf_sat_read_cache(struct wf_sat *sat) | |||
144 | return 0; | 142 | return 0; |
145 | } | 143 | } |
146 | 144 | ||
147 | static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value) | 145 | static int wf_sat_get(struct wf_sensor *sr, s32 *value) |
148 | { | 146 | { |
149 | struct wf_sat_sensor *sens = wf_to_sat(sr); | 147 | struct wf_sat_sensor *sens = wf_to_sat(sr); |
150 | struct wf_sat *sat = sens->sat; | 148 | struct wf_sat *sat = sens->sat; |
@@ -177,34 +175,62 @@ static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value) | |||
177 | return err; | 175 | return err; |
178 | } | 176 | } |
179 | 177 | ||
180 | static void wf_sat_release(struct kref *ref) | 178 | static void wf_sat_release(struct wf_sensor *sr) |
181 | { | ||
182 | struct wf_sat *sat = container_of(ref, struct wf_sat, ref); | ||
183 | |||
184 | if (sat->nr >= 0) | ||
185 | sats[sat->nr] = NULL; | ||
186 | kfree(sat); | ||
187 | } | ||
188 | |||
189 | static void wf_sat_sensor_release(struct wf_sensor *sr) | ||
190 | { | 179 | { |
191 | struct wf_sat_sensor *sens = wf_to_sat(sr); | 180 | struct wf_sat_sensor *sens = wf_to_sat(sr); |
192 | struct wf_sat *sat = sens->sat; | 181 | struct wf_sat *sat = sens->sat; |
193 | 182 | ||
183 | if (atomic_dec_and_test(&sat->refcnt)) { | ||
184 | if (sat->nr >= 0) | ||
185 | sats[sat->nr] = NULL; | ||
186 | kfree(sat); | ||
187 | } | ||
194 | kfree(sens); | 188 | kfree(sens); |
195 | kref_put(&sat->ref, wf_sat_release); | ||
196 | } | 189 | } |
197 | 190 | ||
198 | static struct wf_sensor_ops wf_sat_ops = { | 191 | static struct wf_sensor_ops wf_sat_ops = { |
199 | .get_value = wf_sat_sensor_get, | 192 | .get_value = wf_sat_get, |
200 | .release = wf_sat_sensor_release, | 193 | .release = wf_sat_release, |
201 | .owner = THIS_MODULE, | 194 | .owner = THIS_MODULE, |
202 | }; | 195 | }; |
203 | 196 | ||
197 | static struct i2c_driver wf_sat_driver; | ||
198 | |||
199 | static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) | ||
200 | { | ||
201 | struct i2c_board_info info; | ||
202 | struct i2c_client *client; | ||
203 | const u32 *reg; | ||
204 | u8 addr; | ||
205 | |||
206 | reg = of_get_property(dev, "reg", NULL); | ||
207 | if (reg == NULL) | ||
208 | return; | ||
209 | addr = *reg; | ||
210 | DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr); | ||
211 | |||
212 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
213 | info.addr = (addr >> 1) & 0x7f; | ||
214 | info.platform_data = dev; | ||
215 | strlcpy(info.type, "wf_sat", I2C_NAME_SIZE); | ||
216 | |||
217 | client = i2c_new_device(adapter, &info); | ||
218 | if (client == NULL) { | ||
219 | printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n"); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | /* | ||
224 | * Let i2c-core delete that device on driver removal. | ||
225 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
226 | */ | ||
227 | list_add_tail(&client->detected, &wf_sat_driver.clients); | ||
228 | } | ||
229 | |||
204 | static int wf_sat_probe(struct i2c_client *client, | 230 | static int wf_sat_probe(struct i2c_client *client, |
205 | const struct i2c_device_id *id) | 231 | const struct i2c_device_id *id) |
206 | { | 232 | { |
207 | struct device_node *dev = client->dev.of_node; | 233 | struct device_node *dev = client->dev.platform_data; |
208 | struct wf_sat *sat; | 234 | struct wf_sat *sat; |
209 | struct wf_sat_sensor *sens; | 235 | struct wf_sat_sensor *sens; |
210 | const u32 *reg; | 236 | const u32 *reg; |
@@ -220,10 +246,9 @@ static int wf_sat_probe(struct i2c_client *client, | |||
220 | return -ENOMEM; | 246 | return -ENOMEM; |
221 | sat->nr = -1; | 247 | sat->nr = -1; |
222 | sat->node = of_node_get(dev); | 248 | sat->node = of_node_get(dev); |
223 | kref_init(&sat->ref); | 249 | atomic_set(&sat->refcnt, 0); |
224 | mutex_init(&sat->mutex); | 250 | mutex_init(&sat->mutex); |
225 | sat->i2c = client; | 251 | sat->i2c = client; |
226 | INIT_LIST_HEAD(&sat->sensors); | ||
227 | i2c_set_clientdata(client, sat); | 252 | i2c_set_clientdata(client, sat); |
228 | 253 | ||
229 | vsens[0] = vsens[1] = -1; | 254 | vsens[0] = vsens[1] = -1; |
@@ -285,15 +310,14 @@ static int wf_sat_probe(struct i2c_client *client, | |||
285 | sens->index2 = -1; | 310 | sens->index2 = -1; |
286 | sens->shift = shift; | 311 | sens->shift = shift; |
287 | sens->sat = sat; | 312 | sens->sat = sat; |
313 | atomic_inc(&sat->refcnt); | ||
288 | sens->sens.ops = &wf_sat_ops; | 314 | sens->sens.ops = &wf_sat_ops; |
289 | sens->sens.name = (char *) (sens + 1); | 315 | sens->sens.name = (char *) (sens + 1); |
290 | snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu); | 316 | snprintf(sens->sens.name, 16, "%s-%d", name, cpu); |
291 | 317 | ||
292 | if (wf_register_sensor(&sens->sens)) | 318 | if (wf_register_sensor(&sens->sens)) { |
319 | atomic_dec(&sat->refcnt); | ||
293 | kfree(sens); | 320 | kfree(sens); |
294 | else { | ||
295 | list_add(&sens->link, &sat->sensors); | ||
296 | kref_get(&sat->ref); | ||
297 | } | 321 | } |
298 | } | 322 | } |
299 | 323 | ||
@@ -312,15 +336,14 @@ static int wf_sat_probe(struct i2c_client *client, | |||
312 | sens->index2 = isens[core]; | 336 | sens->index2 = isens[core]; |
313 | sens->shift = 0; | 337 | sens->shift = 0; |
314 | sens->sat = sat; | 338 | sens->sat = sat; |
339 | atomic_inc(&sat->refcnt); | ||
315 | sens->sens.ops = &wf_sat_ops; | 340 | sens->sens.ops = &wf_sat_ops; |
316 | sens->sens.name = (char *) (sens + 1); | 341 | sens->sens.name = (char *) (sens + 1); |
317 | snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu); | 342 | snprintf(sens->sens.name, 16, "cpu-power-%d", cpu); |
318 | 343 | ||
319 | if (wf_register_sensor(&sens->sens)) | 344 | if (wf_register_sensor(&sens->sens)) { |
345 | atomic_dec(&sat->refcnt); | ||
320 | kfree(sens); | 346 | kfree(sens); |
321 | else { | ||
322 | list_add(&sens->link, &sat->sensors); | ||
323 | kref_get(&sat->ref); | ||
324 | } | 347 | } |
325 | } | 348 | } |
326 | 349 | ||
@@ -330,41 +353,61 @@ static int wf_sat_probe(struct i2c_client *client, | |||
330 | return 0; | 353 | return 0; |
331 | } | 354 | } |
332 | 355 | ||
356 | static int wf_sat_attach(struct i2c_adapter *adapter) | ||
357 | { | ||
358 | struct device_node *busnode, *dev = NULL; | ||
359 | struct pmac_i2c_bus *bus; | ||
360 | |||
361 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
362 | if (bus == NULL) | ||
363 | return -ENODEV; | ||
364 | busnode = pmac_i2c_get_bus_node(bus); | ||
365 | |||
366 | while ((dev = of_get_next_child(busnode, dev)) != NULL) | ||
367 | if (of_device_is_compatible(dev, "smu-sat")) | ||
368 | wf_sat_create(adapter, dev); | ||
369 | return 0; | ||
370 | } | ||
371 | |||
333 | static int wf_sat_remove(struct i2c_client *client) | 372 | static int wf_sat_remove(struct i2c_client *client) |
334 | { | 373 | { |
335 | struct wf_sat *sat = i2c_get_clientdata(client); | 374 | struct wf_sat *sat = i2c_get_clientdata(client); |
336 | struct wf_sat_sensor *sens; | ||
337 | 375 | ||
338 | /* release sensors */ | 376 | /* XXX TODO */ |
339 | while(!list_empty(&sat->sensors)) { | ||
340 | sens = list_first_entry(&sat->sensors, | ||
341 | struct wf_sat_sensor, link); | ||
342 | list_del(&sens->link); | ||
343 | wf_unregister_sensor(&sens->sens); | ||
344 | } | ||
345 | sat->i2c = NULL; | ||
346 | i2c_set_clientdata(client, NULL); | ||
347 | kref_put(&sat->ref, wf_sat_release); | ||
348 | 377 | ||
378 | sat->i2c = NULL; | ||
349 | return 0; | 379 | return 0; |
350 | } | 380 | } |
351 | 381 | ||
352 | static const struct i2c_device_id wf_sat_id[] = { | 382 | static const struct i2c_device_id wf_sat_id[] = { |
353 | { "MAC,smu-sat", 0 }, | 383 | { "wf_sat", 0 }, |
354 | { } | 384 | { } |
355 | }; | 385 | }; |
356 | MODULE_DEVICE_TABLE(i2c, wf_sat_id); | ||
357 | 386 | ||
358 | static struct i2c_driver wf_sat_driver = { | 387 | static struct i2c_driver wf_sat_driver = { |
359 | .driver = { | 388 | .driver = { |
360 | .name = "wf_smu_sat", | 389 | .name = "wf_smu_sat", |
361 | }, | 390 | }, |
391 | .attach_adapter = wf_sat_attach, | ||
362 | .probe = wf_sat_probe, | 392 | .probe = wf_sat_probe, |
363 | .remove = wf_sat_remove, | 393 | .remove = wf_sat_remove, |
364 | .id_table = wf_sat_id, | 394 | .id_table = wf_sat_id, |
365 | }; | 395 | }; |
366 | 396 | ||
367 | module_i2c_driver(wf_sat_driver); | 397 | static int __init sat_sensors_init(void) |
398 | { | ||
399 | return i2c_add_driver(&wf_sat_driver); | ||
400 | } | ||
401 | |||
402 | #if 0 /* uncomment when module_exit() below is uncommented */ | ||
403 | static void __exit sat_sensors_exit(void) | ||
404 | { | ||
405 | i2c_del_driver(&wf_sat_driver); | ||
406 | } | ||
407 | #endif | ||
408 | |||
409 | module_init(sat_sensors_init); | ||
410 | /*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */ | ||
368 | 411 | ||
369 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); | 412 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); |
370 | MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control"); | 413 | MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control"); |
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c index 1cc4e4953d8..3c193504bb8 100644 --- a/drivers/macintosh/windfarm_smu_sensors.c +++ b/drivers/macintosh/windfarm_smu_sensors.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <asm/prom.h> | 18 | #include <asm/prom.h> |
19 | #include <asm/machdep.h> | 19 | #include <asm/machdep.h> |
20 | #include <asm/io.h> | 20 | #include <asm/io.h> |
21 | #include <asm/system.h> | ||
21 | #include <asm/sections.h> | 22 | #include <asm/sections.h> |
22 | #include <asm/smu.h> | 23 | #include <asm/smu.h> |
23 | 24 | ||