diff options
Diffstat (limited to 'drivers/macintosh')
41 files changed, 3485 insertions, 835 deletions
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index fa51af11c6f..3067d56b11a 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig @@ -32,7 +32,7 @@ config ADB_MACII config ADB_MACIISI bool "Include Mac IIsi ADB driver" - depends on ADB && MAC + depends on ADB && MAC && BROKEN help Say Y here if want your kernel to support Macintosh systems that use the Mac IIsi style ADB. This includes the IIsi, IIvi, IIvx, Classic @@ -103,6 +103,7 @@ config ADB_PMU_LED_IDE bool "Use front LED as IDE LED by default" depends on ADB_PMU_LED depends on LEDS_CLASS + depends on IDE_GD_ATA select LEDS_TRIGGERS select LEDS_TRIGGER_IDE_DISK help @@ -204,11 +205,14 @@ config THERM_ADT746X better fan behaviour by default, and some manual control. config THERM_PM72 - tristate "Support for thermal management on PowerMac G5" + tristate "Support for thermal management on PowerMac G5 (AGP)" depends on I2C && I2C_POWERMAC && PPC_PMAC64 + default n help This driver provides thermostat and fan control for the desktop - G5 machines. + G5 machines. + + This is deprecated, use windfarm instead. config WINDFARM tristate "New PowerMac thermal control infrastructure" @@ -221,6 +225,22 @@ config WINDFARM_PM81 help This driver provides thermal control for the iMacG5 +config WINDFARM_PM72 + tristate "Support for thermal management on PowerMac G5 (AGP)" + depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU + select I2C_POWERMAC + help + This driver provides thermal control for the PowerMac G5 + "AGP" variants (PowerMac 7,2 and 7,3) + +config WINDFARM_RM31 + tristate "Support for thermal management on Xserve G5" + depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && ADB_PMU + select I2C_POWERMAC + help + This driver provides thermal control for the Xserve G5 + (RackMac3,1) + config WINDFARM_PM91 tristate "Support for thermal management on PowerMac9,1" depends on WINDFARM && I2C && CPU_FREQ_PMAC64 && PMAC_SMU @@ -259,7 +279,7 @@ config PMAC_RACKMETER config SENSORS_AMS tristate "Apple Motion Sensor driver" - depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) && EXPERIMENTAL + depends on PPC_PMAC && !PPC64 && INPUT && ((ADB_PMU && I2C = y) || (ADB_PMU && !I2C) || I2C) select INPUT_POLLDEV help Support for the motion sensor included in PowerBooks. Includes diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index 6652a6ebb6f..d2f0120bc87 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile @@ -29,6 +29,21 @@ obj-$(CONFIG_THERM_PM72) += therm_pm72.o obj-$(CONFIG_THERM_WINDTUNNEL) += therm_windtunnel.o obj-$(CONFIG_THERM_ADT746X) += therm_adt746x.o obj-$(CONFIG_WINDFARM) += windfarm_core.o +obj-$(CONFIG_WINDFARM_PM72) += windfarm_fcu_controls.o \ + windfarm_ad7417_sensor.o \ + windfarm_lm75_sensor.o \ + windfarm_max6690_sensor.o \ + windfarm_pid.o \ + windfarm_cpufreq_clamp.o \ + windfarm_pm72.o +obj-$(CONFIG_WINDFARM_RM31) += windfarm_fcu_controls.o \ + windfarm_ad7417_sensor.o \ + windfarm_lm75_sensor.o \ + windfarm_lm87_sensor.o \ + windfarm_max6690_sensor.o \ + windfarm_pid.o \ + windfarm_cpufreq_clamp.o \ + windfarm_rm31.o obj-$(CONFIG_WINDFARM_PM81) += windfarm_smu_controls.o \ windfarm_smu_sensors.o \ windfarm_lm75_sensor.o windfarm_pid.o \ diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index 75049e76519..9e9c56758a0 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -38,7 +38,7 @@ #include <linux/platform_device.h> #include <linux/mutex.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #ifdef CONFIG_PPC #include <asm/prom.h> #include <asm/machdep.h> @@ -193,8 +193,7 @@ static int adb_scan_bus(void) break; noMovement = 0; - } - else { + } else { /* * No devices left at address i; move the * one(s) we moved to `highFree' back to i. @@ -263,7 +262,7 @@ adb_reset_bus(void) /* * notify clients before sleep */ -static int adb_suspend(struct platform_device *dev, pm_message_t state) +static int __adb_suspend(struct platform_device *dev, pm_message_t state) { adb_got_sleep = 1; /* We need to get a lock on the probe thread */ @@ -276,10 +275,25 @@ static int adb_suspend(struct platform_device *dev, pm_message_t state) return 0; } +static int adb_suspend(struct device *dev) +{ + return __adb_suspend(to_platform_device(dev), PMSG_SUSPEND); +} + +static int adb_freeze(struct device *dev) +{ + return __adb_suspend(to_platform_device(dev), PMSG_FREEZE); +} + +static int adb_poweroff(struct device *dev) +{ + return __adb_suspend(to_platform_device(dev), PMSG_HIBERNATE); +} + /* * reset bus after sleep */ -static int adb_resume(struct platform_device *dev) +static int __adb_resume(struct platform_device *dev) { adb_got_sleep = 0; up(&adb_probe_mutex); @@ -287,6 +301,11 @@ static int adb_resume(struct platform_device *dev) return 0; } + +static int adb_resume(struct device *dev) +{ + return __adb_resume(to_platform_device(dev)); +} #endif /* CONFIG_PM */ static int __init adb_init(void) @@ -502,7 +521,7 @@ void adb_input(unsigned char *buf, int nb, int autopoll) { int i, id; - static int dump_adb_input = 0; + static int dump_adb_input; unsigned long flags; void (*handler)(unsigned char *, int, int); @@ -624,8 +643,7 @@ do_adb_query(struct adb_request *req) { int ret = -EINVAL; - switch(req->data[1]) - { + switch(req->data[1]) { case ADB_QUERY_GETDEVINFO: if (req->nbytes < 3) break; @@ -697,7 +715,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, int ret = 0; struct adbdev_state *state = file->private_data; struct adb_request *req; - wait_queue_t wait = __WAITQUEUE_INITIALIZER(wait,current); + DECLARE_WAITQUEUE(wait, current); unsigned long flags; if (count < 2) @@ -710,7 +728,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, req = NULL; spin_lock_irqsave(&state->lock, flags); add_wait_queue(&state->wait_queue, &wait); - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); for (;;) { req = state->completed; @@ -734,7 +752,7 @@ static ssize_t adb_read(struct file *file, char __user *buf, spin_lock_irqsave(&state->lock, flags); } - current->state = TASK_RUNNING; + set_current_state(TASK_RUNNING); remove_wait_queue(&state->wait_queue, &wait); spin_unlock_irqrestore(&state->lock, flags); @@ -794,8 +812,8 @@ static ssize_t adb_write(struct file *file, const char __user *buf, } /* Special case for ADB_BUSRESET request, all others are sent to the controller */ - else if ((req->data[0] == ADB_PACKET)&&(count > 1) - &&(req->data[1] == ADB_BUSRESET)) { + else if ((req->data[0] == ADB_PACKET) && (count > 1) + && (req->data[1] == ADB_BUSRESET)) { ret = do_adb_reset_bus(); up(&adb_probe_mutex); atomic_dec(&state->n_pending); @@ -831,14 +849,25 @@ static const struct file_operations adb_fops = { .release = adb_release, }; +#ifdef CONFIG_PM +static const struct dev_pm_ops adb_dev_pm_ops = { + .suspend = adb_suspend, + .resume = adb_resume, + /* Hibernate hooks */ + .freeze = adb_freeze, + .thaw = adb_resume, + .poweroff = adb_poweroff, + .restore = adb_resume, +}; +#endif + static struct platform_driver adb_pfdrv = { .driver = { .name = "adb", - }, #ifdef CONFIG_PM - .suspend = adb_suspend, - .resume = adb_resume, + .pm = &adb_dev_pm_ops, #endif + }, }; static struct platform_device adb_pfdev = { diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c index 5396c67ba0a..09d72bb00d1 100644 --- a/drivers/macintosh/adbhid.c +++ b/drivers/macintosh/adbhid.c @@ -328,7 +328,7 @@ adbhid_input_keycode(int id, int scancode, int repeat) switch (keycode) { case ADB_KEY_CAPSLOCK: if (!restore_capslock_events) { - /* Generate down/up events for CapsLock everytime. */ + /* Generate down/up events for CapsLock every time. */ input_report_key(ahid->input, KEY_CAPSLOCK, 1); input_sync(ahid->input); input_report_key(ahid->input, KEY_CAPSLOCK, 0); diff --git a/drivers/macintosh/ams/ams-core.c b/drivers/macintosh/ams/ams-core.c index 399beb1638d..36a4fdddd64 100644 --- a/drivers/macintosh/ams/ams-core.c +++ b/drivers/macintosh/ams/ams-core.c @@ -31,7 +31,7 @@ /* There is only one motion sensor per machine */ struct ams ams_info; -static unsigned int verbose; +static bool verbose; module_param(verbose, bool, 0644); MODULE_PARM_DESC(verbose, "Show free falls and shocks in kernel output"); @@ -226,7 +226,7 @@ void ams_sensor_detach(void) * We do this after ams_info.exit(), because an interrupt might * have arrived before disabling them. */ - flush_work_sync(&ams_info.worker); + flush_work(&ams_info.worker); /* Remove device */ of_device_unregister(ams_info.of_dev); diff --git a/drivers/macintosh/ams/ams-i2c.c b/drivers/macintosh/ams/ams-i2c.c index abeecd27b48..978eda8d667 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, static int ams_i2c_remove(struct i2c_client *client); static const struct i2c_device_id ams_id[] = { - { "ams", 0 }, + { "MAC,accelerometer_1", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ams_id); diff --git a/drivers/macintosh/ams/ams-input.c b/drivers/macintosh/ams/ams-input.c index 8a712392cd3..2edae7dfcab 100644 --- a/drivers/macintosh/ams/ams-input.c +++ b/drivers/macintosh/ams/ams-input.c @@ -19,11 +19,11 @@ #include "ams.h" -static unsigned int joystick; +static bool joystick; module_param(joystick, bool, S_IRUGO); MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); -static unsigned int invert; +static bool invert; module_param(invert, bool, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); @@ -118,8 +118,12 @@ static ssize_t ams_input_store_joystick(struct device *dev, { unsigned long enable; int error = 0; + int ret; - if (strict_strtoul(buf, 0, &enable) || enable > 1) + ret = kstrtoul(buf, 0, &enable); + if (ret) + return ret; + if (enable > 1) return -EINVAL; mutex_lock(&ams_input_mutex); diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c index 6a82388505f..80d30e8e338 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -181,7 +181,7 @@ static void mac_hid_stop_emulation(void) mac_hid_destroy_emumouse(); } -static int mac_hid_toggle_emumouse(ctl_table *table, int write, +static int mac_hid_toggle_emumouse(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -214,7 +214,7 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write, } /* file(s) in /proc/sys/dev/mac_hid */ -static ctl_table mac_hid_files[] = { +static struct ctl_table mac_hid_files[] = { { .procname = "mouse_button_emulation", .data = &mouse_emulate_buttons, @@ -240,7 +240,7 @@ static ctl_table mac_hid_files[] = { }; /* dir in /proc/sys/dev */ -static ctl_table mac_hid_dir[] = { +static struct ctl_table mac_hid_dir[] = { { .procname = "mac_hid", .maxlen = 0, @@ -251,7 +251,7 @@ static ctl_table mac_hid_dir[] = { }; /* /proc/sys/dev itself, in case that is not there yet */ -static ctl_table mac_hid_root_dir[] = { +static struct ctl_table mac_hid_root_dir[] = { { .procname = "dev", .maxlen = 0, diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c index bd6da7a9c55..87de8d9bcfa 100644 --- a/drivers/macintosh/macio-adb.c +++ b/drivers/macintosh/macio-adb.c @@ -14,7 +14,6 @@ #include <asm/pgtable.h> #include <asm/hydra.h> #include <asm/irq.h> -#include <asm/system.h> #include <linux/init.h> #include <linux/ioport.h> @@ -147,7 +146,7 @@ static int macio_adb_reset_bus(void) /* Hrm... we may want to not lock interrupts for so * long ... oh well, who uses that chip anyway ? :) - * That function will be seldomly used during boot + * That function will be seldom used during boot * on rare machines, so... */ spin_lock_irqsave(&macio_lock, flags); diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index 4daf9e5a773..4f12c6f01fe 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -24,6 +24,8 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/machdep.h> #include <asm/macio.h> @@ -137,7 +139,7 @@ extern struct device_attribute macio_dev_attrs[]; struct bus_type macio_bus_type = { .name = "macio", .match = macio_bus_match, - .uevent = of_device_uevent, + .uevent = of_device_uevent_modalias, .probe = macio_device_probe, .remove = macio_device_remove, .shutdown = macio_device_shutdown, @@ -679,7 +681,7 @@ void macio_release_resources(struct macio_dev *dev) #ifdef CONFIG_PCI -static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +static int macio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct device_node* np; struct macio_chip* chip; @@ -739,7 +741,7 @@ static int __devinit macio_pci_probe(struct pci_dev *pdev, const struct pci_devi return 0; } -static void __devexit macio_pci_remove(struct pci_dev* pdev) +static void macio_pci_remove(struct pci_dev* pdev) { panic("removing of macio-asic not supported !\n"); } @@ -748,7 +750,7 @@ static void __devexit macio_pci_remove(struct pci_dev* pdev) * MacIO is matched against any Apple ID, it's probe() function * will then decide wether it applies or not */ -static const struct pci_device_id __devinitdata pci_ids [] = { { +static const struct pci_device_id pci_ids[] = { { .vendor = PCI_VENDOR_ID_APPLE, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c index 2fd435bc542..d98e566a8f5 100644 --- a/drivers/macintosh/mediabay.c +++ b/drivers/macintosh/mediabay.c @@ -63,7 +63,7 @@ struct media_bay_info { int value_count; int timer; struct macio_dev *mdev; - struct mb_ops* ops; + const struct mb_ops* ops; int index; int cached_gpio; int sleeping; @@ -356,7 +356,7 @@ static void poll_media_bay(struct media_bay_info* bay) static char *mb_content_types[] = { "a floppy drive", "a floppy drive", - "an unsuported audio device", + "an unsupported audio device", "an ATA device", "an unsupported PCI device", "an unknown device", @@ -556,7 +556,8 @@ static int media_bay_task(void *x) return 0; } -static int __devinit media_bay_attach(struct macio_dev *mdev, const struct of_device_id *match) +static int media_bay_attach(struct macio_dev *mdev, + const struct of_device_id *match) { struct media_bay_info* bay; u32 __iomem *regbase; @@ -669,7 +670,7 @@ static int media_bay_resume(struct macio_dev *mdev) /* Definitions of "ops" structures. */ -static struct mb_ops ohare_mb_ops = { +static const struct mb_ops ohare_mb_ops = { .name = "Ohare", .content = ohare_mb_content, .power = ohare_mb_power, @@ -678,7 +679,7 @@ static struct mb_ops ohare_mb_ops = { .un_reset_ide = ohare_mb_un_reset_ide, }; -static struct mb_ops heathrow_mb_ops = { +static const struct mb_ops heathrow_mb_ops = { .name = "Heathrow", .content = heathrow_mb_content, .power = heathrow_mb_power, @@ -687,7 +688,7 @@ static struct mb_ops heathrow_mb_ops = { .un_reset_ide = heathrow_mb_un_reset_ide, }; -static struct mb_ops keylargo_mb_ops = { +static const struct mb_ops keylargo_mb_ops = { .name = "KeyLargo", .init = keylargo_mb_init, .content = keylargo_mb_content, diff --git a/drivers/macintosh/nvram.c b/drivers/macintosh/nvram.c index a271c8218d8..f0e03e7937e 100644 --- a/drivers/macintosh/nvram.c +++ b/drivers/macintosh/nvram.c @@ -21,12 +21,16 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) { switch (origin) { + case 0: + break; case 1: offset += file->f_pos; break; case 2: offset += NVRAM_SIZE; break; + default: + offset = -1; } if (offset < 0) return -EINVAL; diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index 39f660b2a60..4192901cab4 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -25,6 +25,8 @@ #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/kernel_stat.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/io.h> #include <asm/prom.h> @@ -81,13 +83,13 @@ static int rackmeter_ignore_nice; */ static inline cputime64_t get_cpu_idle_time(unsigned int cpu) { - cputime64_t retval; + u64 retval; - retval = cputime64_add(kstat_cpu(cpu).cpustat.idle, - kstat_cpu(cpu).cpustat.iowait); + retval = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE] + + kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; if (rackmeter_ignore_nice) - retval = cputime64_add(retval, kstat_cpu(cpu).cpustat.nice); + retval += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE]; return retval; } @@ -220,13 +222,11 @@ static void rackmeter_do_timer(struct work_struct *work) int i, offset, load, cumm, pause; cur_jiffies = jiffies64_to_cputime64(get_jiffies_64()); - total_ticks = (unsigned int)cputime64_sub(cur_jiffies, - rcpu->prev_wall); + total_ticks = (unsigned int) (cur_jiffies - rcpu->prev_wall); rcpu->prev_wall = cur_jiffies; total_idle_ticks = get_cpu_idle_time(cpu); - idle_ticks = (unsigned int) cputime64_sub(total_idle_ticks, - rcpu->prev_idle); + idle_ticks = (unsigned int) (total_idle_ticks - rcpu->prev_idle); rcpu->prev_idle = total_idle_ticks; /* We do a very dumb calculation to update the LEDs for now, @@ -255,7 +255,7 @@ static void rackmeter_do_timer(struct work_struct *work) msecs_to_jiffies(CPU_SAMPLING_RATE)); } -static void __devinit rackmeter_init_cpu_sniffer(struct rackmeter *rm) +static void rackmeter_init_cpu_sniffer(struct rackmeter *rm) { unsigned int cpu; @@ -283,13 +283,13 @@ static void __devinit rackmeter_init_cpu_sniffer(struct rackmeter *rm) } } -static void __devexit rackmeter_stop_cpu_sniffer(struct rackmeter *rm) +static void rackmeter_stop_cpu_sniffer(struct rackmeter *rm) { cancel_delayed_work_sync(&rm->cpu[0].sniffer); cancel_delayed_work_sync(&rm->cpu[1].sniffer); } -static int __devinit rackmeter_setup(struct rackmeter *rm) +static int rackmeter_setup(struct rackmeter *rm) { pr_debug("rackmeter: setting up i2s..\n"); rackmeter_setup_i2s(rm); @@ -364,8 +364,8 @@ static irqreturn_t rackmeter_irq(int irq, void *arg) return IRQ_HANDLED; } -static int __devinit rackmeter_probe(struct macio_dev* mdev, - const struct of_device_id *match) +static int rackmeter_probe(struct macio_dev* mdev, + const struct of_device_id *match) { struct device_node *i2s = NULL, *np = NULL; struct rackmeter *rm = NULL; @@ -523,7 +523,7 @@ static int __devinit rackmeter_probe(struct macio_dev* mdev, return rc; } -static int __devexit rackmeter_remove(struct macio_dev* mdev) +static int rackmeter_remove(struct macio_dev* mdev) { struct rackmeter *rm = dev_get_drvdata(&mdev->ofdev.dev); @@ -590,7 +590,7 @@ static struct macio_driver rackmeter_driver = { .of_match_table = rackmeter_match, }, .probe = rackmeter_probe, - .remove = __devexit_p(rackmeter_remove), + .remove = rackmeter_remove, .shutdown = rackmeter_shutdown, }; diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 290cb325a94..4eab93aa570 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -32,10 +32,10 @@ #include <linux/completion.h> #include <linux/miscdevice.h> #include <linux/delay.h> -#include <linux/sysdev.h> #include <linux/poll.h> #include <linux/mutex.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/slab.h> @@ -46,7 +46,6 @@ #include <asm/pmac_feature.h> #include <asm/smu.h> #include <asm/sections.h> -#include <asm/abs_addr.h> #include <asm/uaccess.h> #define VERSION "0.7" @@ -122,11 +121,7 @@ static void smu_start_cmd(void) DPRINTK("SMU: starting cmd %x, %d bytes data\n", cmd->cmd, cmd->data_len); - DPRINTK("SMU: data buffer: %02x %02x %02x %02x %02x %02x %02x %02x\n", - ((u8 *)cmd->data_buf)[0], ((u8 *)cmd->data_buf)[1], - ((u8 *)cmd->data_buf)[2], ((u8 *)cmd->data_buf)[3], - ((u8 *)cmd->data_buf)[4], ((u8 *)cmd->data_buf)[5], - ((u8 *)cmd->data_buf)[6], ((u8 *)cmd->data_buf)[7]); + DPRINTK("SMU: data buffer: %8ph\n", cmd->data_buf); /* Fill the SMU command buffer */ smu->cmd_buf->cmd = cmd->cmd; @@ -503,7 +498,7 @@ int __init smu_init (void) * 32 bits value safely */ smu->cmd_buf_abs = (u32)smu_cmdbuf_abs; - smu->cmd_buf = (struct smu_cmd_buf *)abs_to_virt(smu_cmdbuf_abs); + smu->cmd_buf = __va(smu_cmdbuf_abs); smu->db_node = of_find_node_by_name(NULL, "smu-doorbell"); if (smu->db_node == NULL) { @@ -567,7 +562,7 @@ fail_msg_node: fail_db_node: of_node_put(smu->db_node); fail_bootmem: - free_bootmem((unsigned long)smu, sizeof(struct smu_device)); + free_bootmem(__pa(smu), sizeof(struct smu_device)); smu = NULL; fail_np: of_node_put(np); @@ -645,8 +640,7 @@ static void smu_expose_childs(struct work_struct *unused) static DECLARE_WORK(smu_expose_childs_work, smu_expose_childs); -static int smu_platform_probe(struct platform_device* dev, - const struct of_device_id *match) +static int smu_platform_probe(struct platform_device* dev) { if (!smu) return -ENODEV; @@ -669,7 +663,7 @@ static const struct of_device_id smu_platform_match[] = {}, }; -static struct of_platform_driver smu_of_platform_driver = +static struct platform_driver smu_of_platform_driver = { .driver = { .name = "smu", @@ -682,14 +676,11 @@ static struct of_platform_driver smu_of_platform_driver = static int __init smu_init_sysfs(void) { /* - * Due to sysfs bogosity, a sysdev is not a real device, so - * we should in fact create both if we want sysdev semantics - * for power management. * For now, we don't power manage machines with an SMU chip, * I'm a bit too far from figuring out how that works with those * new chipsets, but that will come back and bite us */ - of_register_platform_driver(&smu_of_platform_driver); + platform_driver_register(&smu_of_platform_driver); return 0; } @@ -1003,7 +994,7 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id) "%02x !\n", id, hdr->id); goto failure; } - if (prom_add_property(smu->of_node, prop)) { + if (of_add_property(smu->of_node, prop)) { printk(KERN_DEBUG "SMU: Failed creating sdb-partition-%02x " "property !\n", id); goto failure; @@ -1266,7 +1257,8 @@ static unsigned int smu_fpoll(struct file *file, poll_table *wait) if (pp->busy && pp->cmd.status != 1) mask |= POLLIN; spin_unlock_irqrestore(&pp->lock, flags); - } if (pp->mode == smu_file_events) { + } + if (pp->mode == smu_file_events) { /* Not yet implemented */ } return mask; diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index 9e3e2c56659..f433521a6f9 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -29,7 +29,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #undef DEBUG @@ -48,11 +47,11 @@ static u8 FAN_SPD_SET[2] = {0x30, 0x31}; static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ -static const char *sensor_location[3]; +static const char *sensor_location[3] = { "?", "?", "?" }; static int limit_adjust; static int fan_speed = -1; -static int verbose; +static bool verbose; MODULE_AUTHOR("Colin Leroy <colin@colino.net>"); MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and " @@ -80,18 +79,16 @@ struct thermostat { int last_speed[2]; int last_var[2]; int pwm_inv[2]; + struct task_struct *thread; + struct platform_device *pdev; + enum { + ADT7460, + ADT7467 + } type; }; -static enum {ADT7460, ADT7467} therm_type; -static int therm_bus, therm_address; -static struct platform_device * of_dev; -static struct thermostat* thermostat; -static struct task_struct *thread_therm = NULL; - static void write_both_fan_speed(struct thermostat *th, int speed); static void write_fan_speed(struct thermostat *th, int speed, int fan); -static void thermostat_create_files(void); -static void thermostat_remove_files(void); static int write_reg(struct thermostat* th, int reg, u8 data) @@ -127,66 +124,6 @@ read_reg(struct thermostat* th, int reg) return data; } -static struct i2c_driver thermostat_driver; - -static int -attach_thermostat(struct i2c_adapter *adapter) -{ - unsigned long bus_no; - struct i2c_board_info info; - struct i2c_client *client; - - if (strncmp(adapter->name, "uni-n", 5)) - return -ENODEV; - bus_no = simple_strtoul(adapter->name + 6, NULL, 10); - if (bus_no != therm_bus) - return -ENODEV; - - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE); - info.addr = therm_address; - client = i2c_new_device(adapter, &info); - if (!client) - return -ENODEV; - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &thermostat_driver.clients); - return 0; -} - -static int -remove_thermostat(struct i2c_client *client) -{ - struct thermostat *th = i2c_get_clientdata(client); - int i; - - thermostat_remove_files(); - - if (thread_therm != NULL) { - kthread_stop(thread_therm); - } - - printk(KERN_INFO "adt746x: Putting max temperatures back from " - "%d, %d, %d to %d, %d, %d\n", - th->limits[0], th->limits[1], th->limits[2], - th->initial_limits[0], th->initial_limits[1], - th->initial_limits[2]); - - for (i = 0; i < 3; i++) - write_reg(th, LIMIT_REG[i], th->initial_limits[i]); - - write_both_fan_speed(th, -1); - - thermostat = NULL; - - kfree(th); - - return 0; -} - static int read_fan_speed(struct thermostat *th, u8 addr) { u8 tmp[2]; @@ -204,7 +141,7 @@ static int read_fan_speed(struct thermostat *th, u8 addr) static void write_both_fan_speed(struct thermostat *th, int speed) { write_fan_speed(th, speed, 0); - if (therm_type == ADT7460) + if (th->type == ADT7460) write_fan_speed(th, speed, 1); } @@ -217,7 +154,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) else if (speed < -1) speed = 0; - if (therm_type == ADT7467 && fan == 1) + if (th->type == ADT7467 && fan == 1) return; if (th->last_speed[fan] != speed) { @@ -240,7 +177,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) write_reg(th, FAN_SPD_SET[fan], speed); } else { /* back to automatic */ - if(therm_type == ADT7460) { + if(th->type == ADT7460) { manual = read_reg(th, MANUAL_MODE[fan]) & (~MANUAL_MASK); manual &= ~INVERT_MASK; @@ -294,7 +231,7 @@ static void update_fans_speed (struct thermostat *th) /* we don't care about local sensor, so we start at sensor 1 */ for (i = 1; i < 3; i++) { int started = 0; - int fan_number = (therm_type == ADT7460 && i == 2); + int fan_number = (th->type == ADT7460 && i == 2); int var = th->temps[i] - th->limits[i]; if (var > -1) { @@ -371,116 +308,22 @@ static int monitor_task(void *arg) static void set_limit(struct thermostat *th, int i) { - /* Set sensor1 limit higher to avoid powerdowns */ - th->limits[i] = default_limits_chip[i] + limit_adjust; - write_reg(th, LIMIT_REG[i], th->limits[i]); + /* Set sensor1 limit higher to avoid powerdowns */ + th->limits[i] = default_limits_chip[i] + limit_adjust; + write_reg(th, LIMIT_REG[i], th->limits[i]); - /* set our limits to normal */ - th->limits[i] = default_limits_local[i] + limit_adjust; + /* set our limits to normal */ + th->limits[i] = default_limits_local[i] + limit_adjust; } -static int probe_thermostat(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct thermostat* th; - int rc; - int i; - - if (thermostat) - return 0; - - th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); - if (!th) - return -ENOMEM; - - i2c_set_clientdata(client, th); - th->clt = client; - - rc = read_reg(th, CONFIG_REG); - if (rc < 0) { - dev_err(&client->dev, "Thermostat failed to read config!\n"); - kfree(th); - return -ENODEV; - } - - /* force manual control to start the fan quieter */ - if (fan_speed == -1) - fan_speed = 64; - - if(therm_type == ADT7460) { - printk(KERN_INFO "adt746x: ADT7460 initializing\n"); - /* The 7460 needs to be started explicitly */ - write_reg(th, CONFIG_REG, 1); - } else - printk(KERN_INFO "adt746x: ADT7467 initializing\n"); - - for (i = 0; i < 3; i++) { - th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); - set_limit(th, i); - } - - printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" - " to %d, %d, %d\n", - th->initial_limits[0], th->initial_limits[1], - th->initial_limits[2], th->limits[0], th->limits[1], - th->limits[2]); - - thermostat = th; - - /* record invert bit status because fw can corrupt it after suspend */ - th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; - th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; - - /* be sure to really write fan speed the first time */ - th->last_speed[0] = -2; - th->last_speed[1] = -2; - th->last_var[0] = -80; - th->last_var[1] = -80; - - if (fan_speed != -1) { - /* manual mode, stop fans */ - write_both_fan_speed(th, 0); - } else { - /* automatic mode */ - write_both_fan_speed(th, -1); - } - - thread_therm = kthread_run(monitor_task, th, "kfand"); - - if (thread_therm == ERR_PTR(-ENOMEM)) { - printk(KERN_INFO "adt746x: Kthread creation failed\n"); - thread_therm = NULL; - return -ENOMEM; - } - - thermostat_create_files(); - - return 0; +#define BUILD_SHOW_FUNC_INT(name, data) \ +static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct thermostat *th = dev_get_drvdata(dev); \ + return sprintf(buf, "%d\n", data); \ } -static const struct i2c_device_id therm_adt746x_id[] = { - { "therm_adt746x", 0 }, - { } -}; - -static struct i2c_driver thermostat_driver = { - .driver = { - .name = "therm_adt746x", - }, - .attach_adapter = attach_thermostat, - .probe = probe_thermostat, - .remove = remove_thermostat, - .id_table = therm_adt746x_id, -}; - -/* - * Now, unfortunately, sysfs doesn't give us a nice void * we could - * pass around to the attribute functions, so we don't really have - * choice but implement a bunch of them... - * - * FIXME, it does now... - */ -#define BUILD_SHOW_FUNC_INT(name, data) \ +#define BUILD_SHOW_FUNC_INT_LITE(name, data) \ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ { \ return sprintf(buf, "%d\n", data); \ @@ -495,22 +338,24 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch #define BUILD_SHOW_FUNC_FAN(name, data) \ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ { \ + struct thermostat *th = dev_get_drvdata(dev); \ return sprintf(buf, "%d (%d rpm)\n", \ - thermostat->last_speed[data], \ - read_fan_speed(thermostat, FAN_SPEED[data]) \ + th->last_speed[data], \ + read_fan_speed(th, FAN_SPEED[data]) \ ); \ } #define BUILD_STORE_FUNC_DEG(name, data) \ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ { \ + struct thermostat *th = dev_get_drvdata(dev); \ int val; \ int i; \ val = simple_strtol(buf, NULL, 10); \ printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ limit_adjust = val; \ for (i=0; i < 3; i++) \ - set_limit(thermostat, i); \ + set_limit(th, i); \ return n; \ } @@ -526,20 +371,21 @@ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, c return n; \ } -BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(thermostat, TEMP_REG[1]))) -BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(thermostat, TEMP_REG[2]))) -BUILD_SHOW_FUNC_INT(sensor1_limit, thermostat->limits[1]) -BUILD_SHOW_FUNC_INT(sensor2_limit, thermostat->limits[2]) +BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(th, TEMP_REG[1]))) +BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(th, TEMP_REG[2]))) +BUILD_SHOW_FUNC_INT(sensor1_limit, th->limits[1]) +BUILD_SHOW_FUNC_INT(sensor2_limit, th->limits[2]) BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) -BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed) +BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed) +BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) + BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) -BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) -BUILD_SHOW_FUNC_INT(limit_adjust, limit_adjust) -BUILD_STORE_FUNC_DEG(limit_adjust, thermostat) +BUILD_SHOW_FUNC_INT_LITE(limit_adjust, limit_adjust) +BUILD_STORE_FUNC_DEG(limit_adjust, th) static DEVICE_ATTR(sensor1_temperature, S_IRUGO, show_sensor1_temperature,NULL); @@ -565,53 +411,77 @@ static DEVICE_ATTR(sensor2_fan_speed, S_IRUGO, static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, show_limit_adjust, store_limit_adjust); - -static int __init -thermostat_init(void) +static void thermostat_create_files(struct thermostat *th) { - struct device_node* np; - const u32 *prop; - int i = 0, offset = 0; + struct device_node *np = th->clt->dev.of_node; + struct device *dev; + int err; - np = of_find_node_by_name(NULL, "fan"); - if (!np) - return -ENODEV; - if (of_device_is_compatible(np, "adt7460")) - therm_type = ADT7460; - else if (of_device_is_compatible(np, "adt7467")) - therm_type = ADT7467; - else { - of_node_put(np); - return -ENODEV; - } + /* To maintain ABI compatibility with userspace, create + * the old style platform driver and attach the attributes + * to it here + */ + th->pdev = of_platform_device_create(np, "temperatures", NULL); + if (!th->pdev) + return; + dev = &th->pdev->dev; + dev_set_drvdata(dev, th); + err = device_create_file(dev, &dev_attr_sensor1_temperature); + err |= device_create_file(dev, &dev_attr_sensor2_temperature); + err |= device_create_file(dev, &dev_attr_sensor1_limit); + err |= device_create_file(dev, &dev_attr_sensor2_limit); + err |= device_create_file(dev, &dev_attr_sensor1_location); + err |= device_create_file(dev, &dev_attr_sensor2_location); + err |= device_create_file(dev, &dev_attr_limit_adjust); + err |= device_create_file(dev, &dev_attr_specified_fan_speed); + err |= device_create_file(dev, &dev_attr_sensor1_fan_speed); + if(th->type == ADT7460) + err |= device_create_file(dev, &dev_attr_sensor2_fan_speed); + if (err) + printk(KERN_WARNING + "Failed to create temperature attribute file(s).\n"); +} - prop = of_get_property(np, "hwsensor-params-version", NULL); - printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, - (*prop == 1)?"":"un"); - if (*prop != 1) { - of_node_put(np); - return -ENODEV; - } +static void thermostat_remove_files(struct thermostat *th) +{ + struct device *dev; - prop = of_get_property(np, "reg", NULL); - if (!prop) { - of_node_put(np); - return -ENODEV; - } + if (!th->pdev) + return; + dev = &th->pdev->dev; + device_remove_file(dev, &dev_attr_sensor1_temperature); + device_remove_file(dev, &dev_attr_sensor2_temperature); + device_remove_file(dev, &dev_attr_sensor1_limit); + device_remove_file(dev, &dev_attr_sensor2_limit); + device_remove_file(dev, &dev_attr_sensor1_location); + device_remove_file(dev, &dev_attr_sensor2_location); + device_remove_file(dev, &dev_attr_limit_adjust); + device_remove_file(dev, &dev_attr_specified_fan_speed); + device_remove_file(dev, &dev_attr_sensor1_fan_speed); + if (th->type == ADT7460) + device_remove_file(dev, &dev_attr_sensor2_fan_speed); + of_device_unregister(th->pdev); - /* look for bus either by path or using "reg" */ - if (strstr(np->full_name, "/i2c-bus@") != NULL) { - const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9); - therm_bus = tmp_bus[0]-'0'; - } else { - therm_bus = ((*prop) >> 8) & 0x0f; - } +} - therm_address = ((*prop) & 0xff) >> 1; +static int probe_thermostat(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device_node *np = client->dev.of_node; + struct thermostat* th; + const __be32 *prop; + int i, rc, vers, offset = 0; - printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, " - "limit_adjust: %d, fan_speed: %d\n", - therm_bus, therm_address, limit_adjust, fan_speed); + if (!np) + return -ENXIO; + prop = of_get_property(np, "hwsensor-params-version", NULL); + if (!prop) + return -ENXIO; + vers = be32_to_cpup(prop); + printk(KERN_INFO "adt746x: version %d (%ssupported)\n", + vers, vers == 1 ? "" : "un"); + if (vers != 1) + return -ENXIO; if (of_get_property(np, "hwsensor-location", NULL)) { for (i = 0; i < 3; i++) { @@ -624,72 +494,129 @@ thermostat_init(void) printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); offset += strlen(sensor_location[i]) + 1; } - } else { - sensor_location[0] = "?"; - sensor_location[1] = "?"; - sensor_location[2] = "?"; } - of_dev = of_platform_device_create(np, "temperatures", NULL); - of_node_put(np); + th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); + if (!th) + return -ENOMEM; + + i2c_set_clientdata(client, th); + th->clt = client; + th->type = id->driver_data; - if (of_dev == NULL) { - printk(KERN_ERR "Can't register temperatures device !\n"); + rc = read_reg(th, CONFIG_REG); + if (rc < 0) { + dev_err(&client->dev, "Thermostat failed to read config!\n"); + kfree(th); return -ENODEV; } -#ifndef CONFIG_I2C_POWERMAC - request_module("i2c-powermac"); -#endif + /* force manual control to start the fan quieter */ + if (fan_speed == -1) + fan_speed = 64; + + if (th->type == ADT7460) { + printk(KERN_INFO "adt746x: ADT7460 initializing\n"); + /* The 7460 needs to be started explicitly */ + write_reg(th, CONFIG_REG, 1); + } else + printk(KERN_INFO "adt746x: ADT7467 initializing\n"); - return i2c_add_driver(&thermostat_driver); + for (i = 0; i < 3; i++) { + th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); + set_limit(th, i); + } + + printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" + " to %d, %d, %d\n", + th->initial_limits[0], th->initial_limits[1], + th->initial_limits[2], th->limits[0], th->limits[1], + th->limits[2]); + + /* record invert bit status because fw can corrupt it after suspend */ + th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; + th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; + + /* be sure to really write fan speed the first time */ + th->last_speed[0] = -2; + th->last_speed[1] = -2; + th->last_var[0] = -80; + th->last_var[1] = -80; + + if (fan_speed != -1) { + /* manual mode, stop fans */ + write_both_fan_speed(th, 0); + } else { + /* automatic mode */ + write_both_fan_speed(th, -1); + } + + th->thread = kthread_run(monitor_task, th, "kfand"); + if (th->thread == ERR_PTR(-ENOMEM)) { + printk(KERN_INFO "adt746x: Kthread creation failed\n"); + th->thread = NULL; + return -ENOMEM; + } + + thermostat_create_files(th); + + return 0; } -static void thermostat_create_files(void) +static int remove_thermostat(struct i2c_client *client) { - int err; + struct thermostat *th = i2c_get_clientdata(client); + int i; + + thermostat_remove_files(th); - err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_limit); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_location); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_location); - err |= device_create_file(&of_dev->dev, &dev_attr_limit_adjust); - err |= device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed); - err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); - if(therm_type == ADT7460) - err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed); - if (err) - printk(KERN_WARNING - "Failed to create tempertaure attribute file(s).\n"); + if (th->thread != NULL) + kthread_stop(th->thread); + + printk(KERN_INFO "adt746x: Putting max temperatures back from " + "%d, %d, %d to %d, %d, %d\n", + th->limits[0], th->limits[1], th->limits[2], + th->initial_limits[0], th->initial_limits[1], + th->initial_limits[2]); + + for (i = 0; i < 3; i++) + write_reg(th, LIMIT_REG[i], th->initial_limits[i]); + + write_both_fan_speed(th, -1); + + kfree(th); + + return 0; } -static void thermostat_remove_files(void) +static const struct i2c_device_id therm_adt746x_id[] = { + { "MAC,adt7460", ADT7460 }, + { "MAC,adt7467", ADT7467 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, therm_adt746x_id); + +static struct i2c_driver thermostat_driver = { + .driver = { + .name = "therm_adt746x", + }, + .probe = probe_thermostat, + .remove = remove_thermostat, + .id_table = therm_adt746x_id, +}; + +static int __init thermostat_init(void) { - if (of_dev) { - device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature); - device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature); - device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit); - device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit); - device_remove_file(&of_dev->dev, &dev_attr_sensor1_location); - device_remove_file(&of_dev->dev, &dev_attr_sensor2_location); - device_remove_file(&of_dev->dev, &dev_attr_limit_adjust); - device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed); - device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); - - if(therm_type == ADT7460) - device_remove_file(&of_dev->dev, - &dev_attr_sensor2_fan_speed); +#ifndef CONFIG_I2C_POWERMAC + request_module("i2c-powermac"); +#endif - } + return i2c_add_driver(&thermostat_driver); } -static void __exit -thermostat_exit(void) +static void __exit thermostat_exit(void) { i2c_del_driver(&thermostat_driver); - of_device_unregister(of_dev); } module_init(thermostat_init); diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index f3a29f264db..97cfc5ac9fd 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -44,11 +44,11 @@ * TODO: - Check MPU structure version/signature * - Add things like /sbin/overtemp for non-critical * overtemp conditions so userland can take some policy - * decisions, like slewing down CPUs + * decisions, like slowing down CPUs * - Deal with fan and i2c failures in a better way * - Maybe do a generic PID based on params used for * U3 and Drives ? Definitely need to factor code a bit - * bettter... also make sensor detection more robust using + * better... also make sensor detection more robust using * the device-tree to probe for them * - Figure out how to get the slots consumption and set the * slots fan accordingly @@ -91,7 +91,7 @@ * * Mar. 10, 2005 : 1.2 * - Add basic support for Xserve G5 - * - Retreive pumps min/max from EEPROM image in device-tree (broken) + * - Retrieve pumps min/max from EEPROM image in device-tree (broken) * - Use min/max macros here or there * - Latest darwin updated U3H min fan speed to 20% PWM * @@ -127,7 +127,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/macio.h> @@ -153,7 +152,7 @@ static struct i2c_adapter * u3_0; static struct i2c_adapter * u3_1; static struct i2c_adapter * k2; static struct i2c_client * fcu; -static struct cpu_pid_state cpu_state[2]; +static struct cpu_pid_state processor_state[2]; static struct basckside_pid_params backside_params; static struct backside_pid_state backside_state; static struct drives_pid_state drives_state; @@ -375,7 +374,7 @@ static int read_smon_adc(struct cpu_pid_state *state, int chan) rc = i2c_master_send(state->monitor, buf, 2); if (rc <= 0) goto error; - /* Wait for convertion */ + /* Wait for conversion */ msleep(1); /* Switch to data register */ buf[0] = 4; @@ -664,8 +663,8 @@ static int read_eeprom(int cpu, struct mpu_data *out) static void fetch_cpu_pumps_minmax(void) { - struct cpu_pid_state *state0 = &cpu_state[0]; - struct cpu_pid_state *state1 = &cpu_state[1]; + struct cpu_pid_state *state0 = &processor_state[0]; + struct cpu_pid_state *state1 = &processor_state[1]; u16 pump_min = 0, pump_max = 0xffff; u16 tmp[4]; @@ -717,17 +716,17 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch return sprintf(buf, "%d", data); \ } -BUILD_SHOW_FUNC_FIX(cpu0_temperature, cpu_state[0].last_temp) -BUILD_SHOW_FUNC_FIX(cpu0_voltage, cpu_state[0].voltage) -BUILD_SHOW_FUNC_FIX(cpu0_current, cpu_state[0].current_a) -BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, cpu_state[0].rpm) -BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, cpu_state[0].intake_rpm) +BUILD_SHOW_FUNC_FIX(cpu0_temperature, processor_state[0].last_temp) +BUILD_SHOW_FUNC_FIX(cpu0_voltage, processor_state[0].voltage) +BUILD_SHOW_FUNC_FIX(cpu0_current, processor_state[0].current_a) +BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, processor_state[0].rpm) +BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, processor_state[0].intake_rpm) -BUILD_SHOW_FUNC_FIX(cpu1_temperature, cpu_state[1].last_temp) -BUILD_SHOW_FUNC_FIX(cpu1_voltage, cpu_state[1].voltage) -BUILD_SHOW_FUNC_FIX(cpu1_current, cpu_state[1].current_a) -BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, cpu_state[1].rpm) -BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, cpu_state[1].intake_rpm) +BUILD_SHOW_FUNC_FIX(cpu1_temperature, processor_state[1].last_temp) +BUILD_SHOW_FUNC_FIX(cpu1_voltage, processor_state[1].voltage) +BUILD_SHOW_FUNC_FIX(cpu1_current, processor_state[1].current_a) +BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, processor_state[1].rpm) +BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, processor_state[1].intake_rpm) BUILD_SHOW_FUNC_FIX(backside_temperature, backside_state.last_temp) BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm) @@ -919,8 +918,8 @@ static void do_cpu_pid(struct cpu_pid_state *state, s32 temp, s32 power) static void do_monitor_cpu_combined(void) { - struct cpu_pid_state *state0 = &cpu_state[0]; - struct cpu_pid_state *state1 = &cpu_state[1]; + struct cpu_pid_state *state0 = &processor_state[0]; + struct cpu_pid_state *state1 = &processor_state[1]; s32 temp0, power0, temp1, power1; s32 temp_combi, power_combi; int rc, intake, pump; @@ -1150,7 +1149,7 @@ static void do_monitor_cpu_rack(struct cpu_pid_state *state) /* * Initialize the state structure for one CPU control loop */ -static int init_cpu_state(struct cpu_pid_state *state, int index) +static int init_processor_state(struct cpu_pid_state *state, int index) { int err; @@ -1192,7 +1191,7 @@ static int init_cpu_state(struct cpu_pid_state *state, int index) err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm); } if (err) - printk(KERN_WARNING "Failed to create some of the atribute" + printk(KERN_WARNING "Failed to create some of the attribute" "files for CPU %d\n", index); return 0; @@ -1205,7 +1204,7 @@ static int init_cpu_state(struct cpu_pid_state *state, int index) /* * Dispose of the state data for one CPU control loop */ -static void dispose_cpu_state(struct cpu_pid_state *state) +static void dispose_processor_state(struct cpu_pid_state *state) { if (state->monitor == NULL) return; @@ -1804,9 +1803,9 @@ static int main_control_loop(void *x) set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM); /* Initialize ADCs */ - initialize_adc(&cpu_state[0]); - if (cpu_state[1].monitor != NULL) - initialize_adc(&cpu_state[1]); + initialize_adc(&processor_state[0]); + if (processor_state[1].monitor != NULL) + initialize_adc(&processor_state[1]); fcu_tickle_ticks = FCU_TICKLE_TICKS; @@ -1833,14 +1832,14 @@ static int main_control_loop(void *x) if (cpu_pid_type == CPU_PID_TYPE_COMBINED) do_monitor_cpu_combined(); else if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) { - do_monitor_cpu_rack(&cpu_state[0]); - if (cpu_state[1].monitor != NULL) - do_monitor_cpu_rack(&cpu_state[1]); + do_monitor_cpu_rack(&processor_state[0]); + if (processor_state[1].monitor != NULL) + do_monitor_cpu_rack(&processor_state[1]); // better deal with UP } else { - do_monitor_cpu_split(&cpu_state[0]); - if (cpu_state[1].monitor != NULL) - do_monitor_cpu_split(&cpu_state[1]); + do_monitor_cpu_split(&processor_state[0]); + if (processor_state[1].monitor != NULL) + do_monitor_cpu_split(&processor_state[1]); // better deal with UP } /* Then, the rest */ @@ -1885,8 +1884,8 @@ static int main_control_loop(void *x) */ static void dispose_control_loops(void) { - dispose_cpu_state(&cpu_state[0]); - dispose_cpu_state(&cpu_state[1]); + dispose_processor_state(&processor_state[0]); + dispose_processor_state(&processor_state[1]); dispose_backside_state(&backside_state); dispose_drives_state(&drives_state); dispose_slots_state(&slots_state); @@ -1928,12 +1927,12 @@ static int create_control_loops(void) /* Create control loops for everything. If any fail, everything * fails */ - if (init_cpu_state(&cpu_state[0], 0)) + if (init_processor_state(&processor_state[0], 0)) goto fail; if (cpu_pid_type == CPU_PID_TYPE_COMBINED) fetch_cpu_pumps_minmax(); - if (cpu_count > 1 && init_cpu_state(&cpu_state[1], 1)) + if (cpu_count > 1 && init_processor_state(&processor_state[1], 1)) goto fail; if (init_backside_state(&backside_state)) goto fail; @@ -2210,7 +2209,7 @@ static void fcu_lookup_fans(struct device_node *fcu_node) } } -static int fcu_of_probe(struct platform_device* dev, const struct of_device_id *match) +static int fcu_of_probe(struct platform_device* dev) { state = state_detached; of_dev = dev; @@ -2240,7 +2239,7 @@ static const struct of_device_id fcu_match[] = }; MODULE_DEVICE_TABLE(of, fcu_match); -static struct of_platform_driver fcu_of_platform_driver = +static struct platform_driver fcu_of_platform_driver = { .driver = { .name = "temperature", @@ -2263,12 +2262,12 @@ static int __init therm_pm72_init(void) !rackmac) return -ENODEV; - return of_register_platform_driver(&fcu_of_platform_driver); + return platform_driver_register(&fcu_of_platform_driver); } static void __exit therm_pm72_exit(void) { - of_unregister_platform_driver(&fcu_of_platform_driver); + platform_driver_unregister(&fcu_of_platform_driver); } module_init(therm_pm72_init); diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index c89f396e4c5..3b4a157714b 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -41,11 +41,10 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/macio.h> -#define LOG_TEMP 0 /* continously log temperature */ +#define LOG_TEMP 0 /* continuously log temperature */ static struct { volatile int running; @@ -443,8 +442,7 @@ static struct i2c_driver g4fan_driver = { /* initialization / cleanup */ /************************************************************************/ -static int -therm_of_probe( struct platform_device *dev, const struct of_device_id *match ) +static int therm_of_probe(struct platform_device *dev) { return i2c_add_driver( &g4fan_driver ); } @@ -462,7 +460,7 @@ static const struct of_device_id therm_of_match[] = {{ }, {} }; -static struct of_platform_driver therm_of_driver = { +static struct platform_driver therm_of_driver = { .driver = { .name = "temperature", .owner = THIS_MODULE, @@ -509,14 +507,14 @@ g4fan_init( void ) return -ENODEV; } - of_register_platform_driver( &therm_of_driver ); + platform_driver_register( &therm_of_driver ); return 0; } static void __exit g4fan_exit( void ) { - of_unregister_platform_driver( &therm_of_driver ); + platform_driver_unregister( &therm_of_driver ); if( x.of_dev ) of_device_unregister( x.of_dev ); diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index 971bc9582a5..d61f271d220 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -26,7 +26,6 @@ #include <asm/mac_via.h> #endif #include <asm/io.h> -#include <asm/system.h> #include <linux/init.h> static volatile unsigned char __iomem *via; @@ -260,7 +259,7 @@ cuda_probe(void) } while (0) static int -cuda_init_via(void) +__init cuda_init_via(void) { out_8(&via[DIRB], (in_8(&via[DIRB]) | TACK | TIP) & ~TREQ); /* TACK & TIP out */ out_8(&via[B], in_8(&via[B]) | TACK | TIP); /* negate them */ diff --git a/drivers/macintosh/via-macii.c b/drivers/macintosh/via-macii.c index 817f37a875c..3725f088f17 100644 --- a/drivers/macintosh/via-macii.c +++ b/drivers/macintosh/via-macii.c @@ -34,7 +34,6 @@ #include <asm/macintosh.h> #include <asm/macints.h> #include <asm/mac_via.h> -#include <asm/system.h> static volatile unsigned char *via; @@ -159,7 +158,7 @@ int macii_init(void) err = macii_init_via(); if (err) goto out; - err = request_irq(IRQ_MAC_ADB, macii_interrupt, IRQ_FLG_LOCK, "ADB", + err = request_irq(IRQ_MAC_ADB, macii_interrupt, 0, "ADB", macii_interrupt); if (err) goto out; diff --git a/drivers/macintosh/via-maciisi.c b/drivers/macintosh/via-maciisi.c index 9ab5b0c34f0..34d02a91b29 100644 --- a/drivers/macintosh/via-maciisi.c +++ b/drivers/macintosh/via-maciisi.c @@ -122,8 +122,8 @@ maciisi_init(void) return err; } - if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, IRQ_FLG_LOCK | IRQ_FLG_FAST, - "ADB", maciisi_interrupt)) { + if (request_irq(IRQ_MAC_ADB, maciisi_interrupt, 0, "ADB", + maciisi_interrupt)) { printk(KERN_ERR "maciisi_init: can't get irq %d\n", IRQ_MAC_ADB); return -EAGAIN; } diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c index ade1e656bfb..b1d91170ded 100644 --- a/drivers/macintosh/via-pmu-backlight.c +++ b/drivers/macintosh/via-pmu-backlight.c @@ -163,6 +163,7 @@ void __init pmu_backlight_init() snprintf(name, sizeof(name), "pmubl"); memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; props.max_brightness = FB_BACKLIGHT_LEVELS - 1; bd = backlight_device_register(name, NULL, NULL, &pmu_backlight_data, &props); diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index 8b021eb0d48..dee88e59f0d 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -40,17 +40,18 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/device.h> -#include <linux/sysdev.h> +#include <linux/syscore_ops.h> #include <linux/freezer.h> #include <linux/syscalls.h> #include <linux/suspend.h> #include <linux/cpu.h> #include <linux/compat.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/irq.h> #include <asm/pmac_feature.h> @@ -751,8 +752,9 @@ done_battery_state_smart(struct adb_request* req) voltage = (req->reply[8] << 8) | req->reply[9]; break; default: - printk(KERN_WARNING "pmu.c : unrecognized battery info, len: %d, %02x %02x %02x %02x\n", - req->reply_len, req->reply[0], req->reply[1], req->reply[2], req->reply[3]); + pr_warn("pmu.c: unrecognized battery info, " + "len: %d, %4ph\n", req->reply_len, + req->reply); break; } } @@ -870,7 +872,7 @@ static int pmu_battery_proc_show(struct seq_file *m, void *v) static int pmu_battery_proc_open(struct inode *inode, struct file *file) { - return single_open(file, pmu_battery_proc_show, PDE(inode)->data); + return single_open(file, pmu_battery_proc_show, PDE_DATA(inode)); } static const struct file_operations pmu_battery_proc_fops = { @@ -2527,12 +2529,9 @@ void pmu_blink(int n) #if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) int pmu_sys_suspended; -static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state) +static int pmu_syscore_suspend(void) { - if (state.event != PM_EVENT_SUSPEND || pmu_sys_suspended) - return 0; - - /* Suspend PMU event interrupts */\ + /* Suspend PMU event interrupts */ pmu_suspend(); pmu_sys_suspended = 1; @@ -2544,12 +2543,12 @@ static int pmu_sys_suspend(struct sys_device *sysdev, pm_message_t state) return 0; } -static int pmu_sys_resume(struct sys_device *sysdev) +static void pmu_syscore_resume(void) { struct adb_request req; if (!pmu_sys_suspended) - return 0; + return; /* Tell PMU we are ready */ pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2); @@ -2562,50 +2561,21 @@ static int pmu_sys_resume(struct sys_device *sysdev) /* Resume PMU event interrupts */ pmu_resume(); pmu_sys_suspended = 0; - - return 0; } -#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ - -static struct sysdev_class pmu_sysclass = { - .name = "pmu", -}; - -static struct sys_device device_pmu = { - .cls = &pmu_sysclass, -}; - -static struct sysdev_driver driver_pmu = { -#if defined(CONFIG_SUSPEND) && defined(CONFIG_PPC32) - .suspend = &pmu_sys_suspend, - .resume = &pmu_sys_resume, -#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ +static struct syscore_ops pmu_syscore_ops = { + .suspend = pmu_syscore_suspend, + .resume = pmu_syscore_resume, }; -static int __init init_pmu_sysfs(void) +static int pmu_syscore_register(void) { - int rc; + register_syscore_ops(&pmu_syscore_ops); - rc = sysdev_class_register(&pmu_sysclass); - if (rc) { - printk(KERN_ERR "Failed registering PMU sys class\n"); - return -ENODEV; - } - rc = sysdev_register(&device_pmu); - if (rc) { - printk(KERN_ERR "Failed registering PMU sys device\n"); - return -ENODEV; - } - rc = sysdev_driver_register(&pmu_sysclass, &driver_pmu); - if (rc) { - printk(KERN_ERR "Failed registering PMU sys driver\n"); - return -ENODEV; - } return 0; } - -subsys_initcall(init_pmu_sysfs); +subsys_initcall(pmu_syscore_register); +#endif /* CONFIG_SUSPEND && CONFIG_PPC32 */ EXPORT_SYMBOL(pmu_request); EXPORT_SYMBOL(pmu_queue_request); diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c index aeb30d07d5a..a00ee41f057 100644 --- a/drivers/macintosh/via-pmu68k.c +++ b/drivers/macintosh/via-pmu68k.c @@ -37,7 +37,6 @@ #include <asm/mac_via.h> #include <asm/pgtable.h> -#include <asm/system.h> #include <asm/irq.h> #include <asm/uaccess.h> diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h index 7a2482cc26a..028cdac2d33 100644 --- a/drivers/macintosh/windfarm.h +++ b/drivers/macintosh/windfarm.h @@ -17,7 +17,7 @@ #include <linux/device.h> /* Display a 16.16 fixed point value */ -#define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16) +#define FIX32TOPRINT(f) (((s32)(f)) >> 16),(((((s32)(f)) & 0xffff) * 1000) >> 16) /* * Control objects @@ -35,12 +35,13 @@ struct wf_control_ops { }; struct wf_control { - struct list_head link; - struct wf_control_ops *ops; - char *name; - int type; - struct kref ref; - struct device_attribute attr; + struct list_head link; + const struct wf_control_ops *ops; + const char *name; + int type; + struct kref ref; + struct device_attribute attr; + void *priv; }; #define WF_CONTROL_TYPE_GENERIC 0 @@ -72,6 +73,26 @@ static inline int wf_control_set_min(struct wf_control *ct) return ct->ops->set_value(ct, vmin); } +static inline int wf_control_set(struct wf_control *ct, s32 val) +{ + return ct->ops->set_value(ct, val); +} + +static inline int wf_control_get(struct wf_control *ct, s32 *val) +{ + return ct->ops->get_value(ct, val); +} + +static inline s32 wf_control_get_min(struct wf_control *ct) +{ + return ct->ops->get_min(ct); +} + +static inline s32 wf_control_get_max(struct wf_control *ct) +{ + return ct->ops->get_max(ct); +} + /* * Sensor objects */ @@ -85,11 +106,12 @@ struct wf_sensor_ops { }; struct wf_sensor { - struct list_head link; - struct wf_sensor_ops *ops; - char *name; - struct kref ref; - struct device_attribute attr; + struct list_head link; + const struct wf_sensor_ops *ops; + const char *name; + struct kref ref; + struct device_attribute attr; + void *priv; }; /* Same lifetime rules as controls */ @@ -99,6 +121,11 @@ extern struct wf_sensor * wf_find_sensor(const char *name); extern int wf_get_sensor(struct wf_sensor *sr); extern void wf_put_sensor(struct wf_sensor *sr); +static inline int wf_sensor_get(struct wf_sensor *sr, s32 *val) +{ + return sr->ops->get_value(sr, val); +} + /* For use by clients. Note that we are a bit racy here since * notifier_block doesn't have a module owner field. I may fix * it one day ... diff --git a/drivers/macintosh/windfarm_ad7417_sensor.c b/drivers/macintosh/windfarm_ad7417_sensor.c new file mode 100644 index 00000000000..7c28b71246c --- /dev/null +++ b/drivers/macintosh/windfarm_ad7417_sensor.c @@ -0,0 +1,347 @@ +/* + * Windfarm PowerMac thermal control. AD7417 sensors + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/i2c.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/io.h> +#include <asm/sections.h> + +#include "windfarm.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +struct wf_ad7417_priv { + struct kref ref; + struct i2c_client *i2c; + u8 config; + u8 cpu; + const struct mpu_data *mpu; + struct wf_sensor sensors[5]; + struct mutex lock; +}; + +static int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value) +{ + struct wf_ad7417_priv *pv = sr->priv; + u8 buf[2]; + s16 raw; + int rc; + + *value = 0; + mutex_lock(&pv->lock); + + /* Read temp register */ + buf[0] = 0; + rc = i2c_master_send(pv->i2c, buf, 1); + if (rc < 0) + goto error; + rc = i2c_master_recv(pv->i2c, buf, 2); + if (rc < 0) + goto error; + + /* Read a a 16-bit signed value */ + raw = be16_to_cpup((__le16 *)buf); + + /* Convert 8.8-bit to 16.16 fixed point */ + *value = ((s32)raw) << 8; + + mutex_unlock(&pv->lock); + return 0; + +error: + mutex_unlock(&pv->lock); + return -1; +} + +/* + * Scaling factors for the AD7417 ADC converters (except + * for the CPU diode which is obtained from the EEPROM). + * Those values are obtained from the property list of + * the darwin driver + */ +#define ADC_12V_CURRENT_SCALE 0x0320 /* _AD2 */ +#define ADC_CPU_VOLTAGE_SCALE 0x00a0 /* _AD3 */ +#define ADC_CPU_CURRENT_SCALE 0x1f40 /* _AD4 */ + +static void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv, + int chan, s32 raw, s32 *value) +{ + switch(chan) { + case 1: /* Diode */ + *value = (raw * (s32)pv->mpu->mdiode + + ((s32)pv->mpu->bdiode << 12)) >> 2; + break; + case 2: /* 12v current */ + *value = raw * ADC_12V_CURRENT_SCALE; + break; + case 3: /* core voltage */ + *value = raw * ADC_CPU_VOLTAGE_SCALE; + break; + case 4: /* core current */ + *value = raw * ADC_CPU_CURRENT_SCALE; + break; + } +} + +static int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value) +{ + struct wf_ad7417_priv *pv = sr->priv; + int chan = sr - pv->sensors; + int i, rc; + u8 buf[2]; + u16 raw; + + *value = 0; + mutex_lock(&pv->lock); + for (i = 0; i < 10; i++) { + /* Set channel */ + buf[0] = 1; + buf[1] = (pv->config & 0x1f) | (chan << 5); + rc = i2c_master_send(pv->i2c, buf, 2); + if (rc < 0) + goto error; + + /* Wait for conversion */ + msleep(1); + + /* Switch to data register */ + buf[0] = 4; + rc = i2c_master_send(pv->i2c, buf, 1); + if (rc < 0) + goto error; + + /* Read result */ + rc = i2c_master_recv(pv->i2c, buf, 2); + if (rc < 0) + goto error; + + /* Read a a 16-bit signed value */ + raw = be16_to_cpup((__le16 *)buf) >> 6; + wf_ad7417_adc_convert(pv, chan, raw, value); + + dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]" + " raw value: 0x%x, conv to: 0x%08x\n", + chan, sr->name, raw, *value); + + mutex_unlock(&pv->lock); + return 0; + + error: + dev_dbg(&pv->i2c->dev, + "Error reading ADC, try %d...\n", i); + if (i < 9) + msleep(10); + } + mutex_unlock(&pv->lock); + return -1; +} + +static void wf_ad7417_release(struct kref *ref) +{ + struct wf_ad7417_priv *pv = container_of(ref, + struct wf_ad7417_priv, ref); + kfree(pv); +} + +static void wf_ad7417_sensor_release(struct wf_sensor *sr) +{ + struct wf_ad7417_priv *pv = sr->priv; + + kfree(sr->name); + kref_put(&pv->ref, wf_ad7417_release); +} + +static const struct wf_sensor_ops wf_ad7417_temp_ops = { + .get_value = wf_ad7417_temp_get, + .release = wf_ad7417_sensor_release, + .owner = THIS_MODULE, +}; + +static const struct wf_sensor_ops wf_ad7417_adc_ops = { + .get_value = wf_ad7417_adc_get, + .release = wf_ad7417_sensor_release, + .owner = THIS_MODULE, +}; + +static void wf_ad7417_add_sensor(struct wf_ad7417_priv *pv, + int index, const char *name, + const struct wf_sensor_ops *ops) +{ + pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu); + pv->sensors[index].priv = pv; + pv->sensors[index].ops = ops; + if (!wf_register_sensor(&pv->sensors[index])) + kref_get(&pv->ref); +} + +static void wf_ad7417_init_chip(struct wf_ad7417_priv *pv) +{ + int rc; + u8 buf[2]; + u8 config = 0; + + /* + * Read ADC the configuration register and cache it. We + * also make sure Config2 contains proper values, I've seen + * cases where we got stale grabage in there, thus preventing + * proper reading of conv. values + */ + + /* Clear Config2 */ + buf[0] = 5; + buf[1] = 0; + i2c_master_send(pv->i2c, buf, 2); + + /* Read & cache Config1 */ + buf[0] = 1; + rc = i2c_master_send(pv->i2c, buf, 1); + if (rc > 0) { + rc = i2c_master_recv(pv->i2c, buf, 1); + if (rc > 0) { + config = buf[0]; + + dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n", + config); + + /* Disable shutdown mode */ + config &= 0xfe; + buf[0] = 1; + buf[1] = config; + rc = i2c_master_send(pv->i2c, buf, 2); + } + } + if (rc <= 0) + dev_err(&pv->i2c->dev, "Error reading ADC config\n"); + + pv->config = config; +} + +static int wf_ad7417_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wf_ad7417_priv *pv; + const struct mpu_data *mpu; + const char *loc; + int cpu_nr; + + loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); + if (!loc) { + dev_warn(&client->dev, "Missing hwsensor-location property!\n"); + return -ENXIO; + } + + /* + * Identify which CPU we belong to by looking at the first entry + * in the hwsensor-location list + */ + if (!strncmp(loc, "CPU A", 5)) + cpu_nr = 0; + else if (!strncmp(loc, "CPU B", 5)) + cpu_nr = 1; + else { + pr_err("wf_ad7417: Can't identify location %s\n", loc); + return -ENXIO; + } + mpu = wf_get_mpu(cpu_nr); + if (!mpu) { + dev_err(&client->dev, "Failed to retrieve MPU data\n"); + return -ENXIO; + } + + pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL); + if (pv == NULL) + return -ENODEV; + + kref_init(&pv->ref); + mutex_init(&pv->lock); + pv->i2c = client; + pv->cpu = cpu_nr; + pv->mpu = mpu; + dev_set_drvdata(&client->dev, pv); + + /* Initialize the chip */ + wf_ad7417_init_chip(pv); + + /* + * We cannot rely on Apple device-tree giving us child + * node with the names of the individual sensors so we + * just hard code what we know about them + */ + wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops); + wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops); + wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops); + wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops); + wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops); + + return 0; +} + +static int wf_ad7417_remove(struct i2c_client *client) +{ + struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev); + int i; + + /* Mark client detached */ + pv->i2c = NULL; + + /* Release sensor */ + for (i = 0; i < 5; i++) + wf_unregister_sensor(&pv->sensors[i]); + + kref_put(&pv->ref, wf_ad7417_release); + + return 0; +} + +static const struct i2c_device_id wf_ad7417_id[] = { + { "MAC,ad7417", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wf_ad7417_id); + +static struct i2c_driver wf_ad7417_driver = { + .driver = { + .name = "wf_ad7417", + }, + .probe = wf_ad7417_probe, + .remove = wf_ad7417_remove, + .id_table = wf_ad7417_id, +}; + +static int wf_ad7417_init(void) +{ + /* This is only supported on these machines */ + if (!of_machine_is_compatible("PowerMac7,2") && + !of_machine_is_compatible("PowerMac7,3") && + !of_machine_is_compatible("RackMac3,1")) + return -ENODEV; + + return i2c_add_driver(&wf_ad7417_driver); +} + +static void wf_ad7417_exit(void) +{ + i2c_del_driver(&wf_ad7417_driver); +} + +module_init(wf_ad7417_init); +module_exit(wf_ad7417_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index ce8897933a8..3ee198b6584 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -164,13 +164,27 @@ static ssize_t wf_show_control(struct device *dev, struct device_attribute *attr, char *buf) { struct wf_control *ctrl = container_of(attr, struct wf_control, attr); + const char *typestr; s32 val = 0; int err; err = ctrl->ops->get_value(ctrl, &val); - if (err < 0) + if (err < 0) { + if (err == -EFAULT) + return sprintf(buf, "<HW FAULT>\n"); return err; - return sprintf(buf, "%d\n", val); + } + switch(ctrl->type) { + case WF_CONTROL_RPM_FAN: + typestr = " RPM"; + break; + case WF_CONTROL_PWM_FAN: + typestr = " %"; + break; + default: + typestr = ""; + } + return sprintf(buf, "%d%s\n", val, typestr); } /* This is really only for debugging... */ @@ -470,11 +484,6 @@ static int __init windfarm_core_init(void) { DBG("wf: core loaded\n"); - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; platform_device_register(&wf_platform_device); return 0; } diff --git a/drivers/macintosh/windfarm_cpufreq_clamp.c b/drivers/macintosh/windfarm_cpufreq_clamp.c index 1a77a7c97d0..72d1fdfe02a 100644 --- a/drivers/macintosh/windfarm_cpufreq_clamp.c +++ b/drivers/macintosh/windfarm_cpufreq_clamp.c @@ -75,12 +75,6 @@ static int __init wf_cpufreq_clamp_init(void) { struct wf_control *clamp; - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; - clamp = kmalloc(sizeof(struct wf_control), GFP_KERNEL); if (clamp == NULL) return -ENOMEM; diff --git a/drivers/macintosh/windfarm_fcu_controls.c b/drivers/macintosh/windfarm_fcu_controls.c new file mode 100644 index 00000000000..0226b796a21 --- /dev/null +++ b/drivers/macintosh/windfarm_fcu_controls.c @@ -0,0 +1,600 @@ +/* + * Windfarm PowerMac thermal control. FCU fan control + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + */ +#undef DEBUG + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/i2c.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/io.h> +#include <asm/sections.h> + +#include "windfarm.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +/* + * This option is "weird" :) Basically, if you define this to 1 + * the control loop for the RPMs fans (not PWMs) will apply the + * correction factor obtained from the PID to the actual RPM + * speed read from the FCU. + * + * If you define the below constant to 0, then it will be + * applied to the setpoint RPM speed, that is basically the + * speed we proviously "asked" for. + * + * I'm using 0 for now which is what therm_pm72 used to do and + * what Darwin -apparently- does based on observed behaviour. + */ +#define RPM_PID_USE_ACTUAL_SPEED 0 + +/* Default min/max for pumps */ +#define CPU_PUMP_OUTPUT_MAX 3200 +#define CPU_PUMP_OUTPUT_MIN 1250 + +#define FCU_FAN_RPM 0 +#define FCU_FAN_PWM 1 + +struct wf_fcu_priv { + struct kref ref; + struct i2c_client *i2c; + struct mutex lock; + struct list_head fan_list; + int rpm_shift; +}; + +struct wf_fcu_fan { + struct list_head link; + int id; + s32 min, max, target; + struct wf_fcu_priv *fcu_priv; + struct wf_control ctrl; +}; + +static void wf_fcu_release(struct kref *ref) +{ + struct wf_fcu_priv *pv = container_of(ref, struct wf_fcu_priv, ref); + + kfree(pv); +} + +static void wf_fcu_fan_release(struct wf_control *ct) +{ + struct wf_fcu_fan *fan = ct->priv; + + kref_put(&fan->fcu_priv->ref, wf_fcu_release); + kfree(fan); +} + +static int wf_fcu_read_reg(struct wf_fcu_priv *pv, int reg, + unsigned char *buf, int nb) +{ + int tries, nr, nw; + + mutex_lock(&pv->lock); + + buf[0] = reg; + tries = 0; + for (;;) { + nw = i2c_master_send(pv->i2c, buf, 1); + if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) + break; + msleep(10); + ++tries; + } + if (nw <= 0) { + pr_err("Failure writing address to FCU: %d", nw); + nr = nw; + goto bail; + } + tries = 0; + for (;;) { + nr = i2c_master_recv(pv->i2c, buf, nb); + if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100) + break; + msleep(10); + ++tries; + } + if (nr <= 0) + pr_err("wf_fcu: Failure reading data from FCU: %d", nw); + bail: + mutex_unlock(&pv->lock); + return nr; +} + +static int wf_fcu_write_reg(struct wf_fcu_priv *pv, int reg, + const unsigned char *ptr, int nb) +{ + int tries, nw; + unsigned char buf[16]; + + buf[0] = reg; + memcpy(buf+1, ptr, nb); + ++nb; + tries = 0; + for (;;) { + nw = i2c_master_send(pv->i2c, buf, nb); + if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) + break; + msleep(10); + ++tries; + } + if (nw < 0) + pr_err("wf_fcu: Failure writing to FCU: %d", nw); + return nw; +} + +static int wf_fcu_fan_set_rpm(struct wf_control *ct, s32 value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + int rc, shift = pv->rpm_shift; + unsigned char buf[2]; + + if (value < fan->min) + value = fan->min; + if (value > fan->max) + value = fan->max; + + fan->target = value; + + buf[0] = value >> (8 - shift); + buf[1] = value << shift; + rc = wf_fcu_write_reg(pv, 0x10 + (fan->id * 2), buf, 2); + if (rc < 0) + return -EIO; + return 0; +} + +static int wf_fcu_fan_get_rpm(struct wf_control *ct, s32 *value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + int rc, reg_base, shift = pv->rpm_shift; + unsigned char failure; + unsigned char active; + unsigned char buf[2]; + + rc = wf_fcu_read_reg(pv, 0xb, &failure, 1); + if (rc != 1) + return -EIO; + if ((failure & (1 << fan->id)) != 0) + return -EFAULT; + rc = wf_fcu_read_reg(pv, 0xd, &active, 1); + if (rc != 1) + return -EIO; + if ((active & (1 << fan->id)) == 0) + return -ENXIO; + + /* Programmed value or real current speed */ +#if RPM_PID_USE_ACTUAL_SPEED + reg_base = 0x11; +#else + reg_base = 0x10; +#endif + rc = wf_fcu_read_reg(pv, reg_base + (fan->id * 2), buf, 2); + if (rc != 2) + return -EIO; + + *value = (buf[0] << (8 - shift)) | buf[1] >> shift; + + return 0; +} + +static int wf_fcu_fan_set_pwm(struct wf_control *ct, s32 value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + unsigned char buf[2]; + int rc; + + if (value < fan->min) + value = fan->min; + if (value > fan->max) + value = fan->max; + + fan->target = value; + + value = (value * 2559) / 1000; + buf[0] = value; + rc = wf_fcu_write_reg(pv, 0x30 + (fan->id * 2), buf, 1); + if (rc < 0) + return -EIO; + return 0; +} + +static int wf_fcu_fan_get_pwm(struct wf_control *ct, s32 *value) +{ + struct wf_fcu_fan *fan = ct->priv; + struct wf_fcu_priv *pv = fan->fcu_priv; + unsigned char failure; + unsigned char active; + unsigned char buf[2]; + int rc; + + rc = wf_fcu_read_reg(pv, 0x2b, &failure, 1); + if (rc != 1) + return -EIO; + if ((failure & (1 << fan->id)) != 0) + return -EFAULT; + rc = wf_fcu_read_reg(pv, 0x2d, &active, 1); + if (rc != 1) + return -EIO; + if ((active & (1 << fan->id)) == 0) + return -ENXIO; + + rc = wf_fcu_read_reg(pv, 0x30 + (fan->id * 2), buf, 1); + if (rc != 1) + return -EIO; + + *value = (((s32)buf[0]) * 1000) / 2559; + + return 0; +} + +static s32 wf_fcu_fan_min(struct wf_control *ct) +{ + struct wf_fcu_fan *fan = ct->priv; + + return fan->min; +} + +static s32 wf_fcu_fan_max(struct wf_control *ct) +{ + struct wf_fcu_fan *fan = ct->priv; + + return fan->max; +} + +static const struct wf_control_ops wf_fcu_fan_rpm_ops = { + .set_value = wf_fcu_fan_set_rpm, + .get_value = wf_fcu_fan_get_rpm, + .get_min = wf_fcu_fan_min, + .get_max = wf_fcu_fan_max, + .release = wf_fcu_fan_release, + .owner = THIS_MODULE, +}; + +static const struct wf_control_ops wf_fcu_fan_pwm_ops = { + .set_value = wf_fcu_fan_set_pwm, + .get_value = wf_fcu_fan_get_pwm, + .get_min = wf_fcu_fan_min, + .get_max = wf_fcu_fan_max, + .release = wf_fcu_fan_release, + .owner = THIS_MODULE, +}; + +static void wf_fcu_get_pump_minmax(struct wf_fcu_fan *fan) +{ + const struct mpu_data *mpu = wf_get_mpu(0); + u16 pump_min = 0, pump_max = 0xffff; + u16 tmp[4]; + + /* Try to fetch pumps min/max infos from eeprom */ + if (mpu) { + memcpy(&tmp, mpu->processor_part_num, 8); + if (tmp[0] != 0xffff && tmp[1] != 0xffff) { + pump_min = max(pump_min, tmp[0]); + pump_max = min(pump_max, tmp[1]); + } + if (tmp[2] != 0xffff && tmp[3] != 0xffff) { + pump_min = max(pump_min, tmp[2]); + pump_max = min(pump_max, tmp[3]); + } + } + + /* Double check the values, this _IS_ needed as the EEPROM on + * some dual 2.5Ghz G5s seem, at least, to have both min & max + * same to the same value ... (grrrr) + */ + if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) { + pump_min = CPU_PUMP_OUTPUT_MIN; + pump_max = CPU_PUMP_OUTPUT_MAX; + } + + fan->min = pump_min; + fan->max = pump_max; + + DBG("wf_fcu: pump min/max for %s set to: [%d..%d] RPM\n", + fan->ctrl.name, pump_min, pump_max); +} + +static void wf_fcu_get_rpmfan_minmax(struct wf_fcu_fan *fan) +{ + struct wf_fcu_priv *pv = fan->fcu_priv; + const struct mpu_data *mpu0 = wf_get_mpu(0); + const struct mpu_data *mpu1 = wf_get_mpu(1); + + /* Default */ + fan->min = 2400 >> pv->rpm_shift; + fan->max = 56000 >> pv->rpm_shift; + + /* CPU fans have min/max in MPU */ + if (mpu0 && !strcmp(fan->ctrl.name, "cpu-front-fan-0")) { + fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); + fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); + goto bail; + } + if (mpu1 && !strcmp(fan->ctrl.name, "cpu-front-fan-1")) { + fan->min = max(fan->min, (s32)mpu1->rminn_intake_fan); + fan->max = min(fan->max, (s32)mpu1->rmaxn_intake_fan); + goto bail; + } + if (mpu0 && !strcmp(fan->ctrl.name, "cpu-rear-fan-0")) { + fan->min = max(fan->min, (s32)mpu0->rminn_exhaust_fan); + fan->max = min(fan->max, (s32)mpu0->rmaxn_exhaust_fan); + goto bail; + } + if (mpu1 && !strcmp(fan->ctrl.name, "cpu-rear-fan-1")) { + fan->min = max(fan->min, (s32)mpu1->rminn_exhaust_fan); + fan->max = min(fan->max, (s32)mpu1->rmaxn_exhaust_fan); + goto bail; + } + /* Rackmac variants, we just use mpu0 intake */ + if (!strncmp(fan->ctrl.name, "cpu-fan", 7)) { + fan->min = max(fan->min, (s32)mpu0->rminn_intake_fan); + fan->max = min(fan->max, (s32)mpu0->rmaxn_intake_fan); + goto bail; + } + bail: + DBG("wf_fcu: fan min/max for %s set to: [%d..%d] RPM\n", + fan->ctrl.name, fan->min, fan->max); +} + +static void wf_fcu_add_fan(struct wf_fcu_priv *pv, const char *name, + int type, int id) +{ + struct wf_fcu_fan *fan; + + fan = kzalloc(sizeof(*fan), GFP_KERNEL); + if (!fan) + return; + fan->fcu_priv = pv; + fan->id = id; + fan->ctrl.name = name; + fan->ctrl.priv = fan; + + /* min/max is oddball but the code comes from + * therm_pm72 which seems to work so ... + */ + if (type == FCU_FAN_RPM) { + if (!strncmp(name, "cpu-pump", strlen("cpu-pump"))) + wf_fcu_get_pump_minmax(fan); + else + wf_fcu_get_rpmfan_minmax(fan); + fan->ctrl.type = WF_CONTROL_RPM_FAN; + fan->ctrl.ops = &wf_fcu_fan_rpm_ops; + } else { + fan->min = 10; + fan->max = 100; + fan->ctrl.type = WF_CONTROL_PWM_FAN; + fan->ctrl.ops = &wf_fcu_fan_pwm_ops; + } + + if (wf_register_control(&fan->ctrl)) { + pr_err("wf_fcu: Failed to register fan %s\n", name); + kfree(fan); + return; + } + list_add(&fan->link, &pv->fan_list); + kref_get(&pv->ref); +} + +static void wf_fcu_lookup_fans(struct wf_fcu_priv *pv) +{ + /* Translation of device-tree location properties to + * windfarm fan names + */ + static const struct { + const char *dt_name; /* Device-tree name */ + const char *ct_name; /* Control name */ + } loc_trans[] = { + { "BACKSIDE", "backside-fan", }, + { "SYS CTRLR FAN", "backside-fan", }, + { "DRIVE BAY", "drive-bay-fan", }, + { "SLOT", "slots-fan", }, + { "PCI FAN", "slots-fan", }, + { "CPU A INTAKE", "cpu-front-fan-0", }, + { "CPU A EXHAUST", "cpu-rear-fan-0", }, + { "CPU B INTAKE", "cpu-front-fan-1", }, + { "CPU B EXHAUST", "cpu-rear-fan-1", }, + { "CPU A PUMP", "cpu-pump-0", }, + { "CPU B PUMP", "cpu-pump-1", }, + { "CPU A 1", "cpu-fan-a-0", }, + { "CPU A 2", "cpu-fan-b-0", }, + { "CPU A 3", "cpu-fan-c-0", }, + { "CPU B 1", "cpu-fan-a-1", }, + { "CPU B 2", "cpu-fan-b-1", }, + { "CPU B 3", "cpu-fan-c-1", }, + }; + struct device_node *np = NULL, *fcu = pv->i2c->dev.of_node; + int i; + + DBG("Looking up FCU controls in device-tree...\n"); + + while ((np = of_get_next_child(fcu, np)) != NULL) { + int id, type = -1; + const char *loc; + const char *name; + const u32 *reg; + + DBG(" control: %s, type: %s\n", np->name, np->type); + + /* Detect control type */ + if (!strcmp(np->type, "fan-rpm-control") || + !strcmp(np->type, "fan-rpm")) + type = FCU_FAN_RPM; + if (!strcmp(np->type, "fan-pwm-control") || + !strcmp(np->type, "fan-pwm")) + type = FCU_FAN_PWM; + /* Only care about fans for now */ + if (type == -1) + continue; + + /* Lookup for a matching location */ + loc = of_get_property(np, "location", NULL); + reg = of_get_property(np, "reg", NULL); + if (loc == NULL || reg == NULL) + continue; + DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); + + for (i = 0; i < ARRAY_SIZE(loc_trans); i++) { + if (strncmp(loc, loc_trans[i].dt_name, + strlen(loc_trans[i].dt_name))) + continue; + name = loc_trans[i].ct_name; + + DBG(" location match, name: %s\n", name); + + if (type == FCU_FAN_RPM) + id = ((*reg) - 0x10) / 2; + else + id = ((*reg) - 0x30) / 2; + if (id > 7) { + pr_warning("wf_fcu: Can't parse " + "fan ID in device-tree for %s\n", + np->full_name); + break; + } + wf_fcu_add_fan(pv, name, type, id); + break; + } + } +} + +static void wf_fcu_default_fans(struct wf_fcu_priv *pv) +{ + /* We only support the default fans for PowerMac7,2 */ + if (!of_machine_is_compatible("PowerMac7,2")) + return; + + wf_fcu_add_fan(pv, "backside-fan", FCU_FAN_PWM, 1); + wf_fcu_add_fan(pv, "drive-bay-fan", FCU_FAN_RPM, 2); + wf_fcu_add_fan(pv, "slots-fan", FCU_FAN_PWM, 2); + wf_fcu_add_fan(pv, "cpu-front-fan-0", FCU_FAN_RPM, 3); + wf_fcu_add_fan(pv, "cpu-rear-fan-0", FCU_FAN_RPM, 4); + wf_fcu_add_fan(pv, "cpu-front-fan-1", FCU_FAN_RPM, 5); + wf_fcu_add_fan(pv, "cpu-rear-fan-1", FCU_FAN_RPM, 6); +} + +static int wf_fcu_init_chip(struct wf_fcu_priv *pv) +{ + unsigned char buf = 0xff; + int rc; + + rc = wf_fcu_write_reg(pv, 0xe, &buf, 1); + if (rc < 0) + return -EIO; + rc = wf_fcu_write_reg(pv, 0x2e, &buf, 1); + if (rc < 0) + return -EIO; + rc = wf_fcu_read_reg(pv, 0, &buf, 1); + if (rc < 0) + return -EIO; + pv->rpm_shift = (buf == 1) ? 2 : 3; + + pr_debug("wf_fcu: FCU Initialized, RPM fan shift is %d\n", + pv->rpm_shift); + + return 0; +} + +static int wf_fcu_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wf_fcu_priv *pv; + + pv = kzalloc(sizeof(*pv), GFP_KERNEL); + if (!pv) + return -ENOMEM; + + kref_init(&pv->ref); + mutex_init(&pv->lock); + INIT_LIST_HEAD(&pv->fan_list); + pv->i2c = client; + + /* + * First we must start the FCU which will query the + * shift value to apply to RPMs + */ + if (wf_fcu_init_chip(pv)) { + pr_err("wf_fcu: Initialization failed !\n"); + kfree(pv); + return -ENXIO; + } + + /* First lookup fans in the device-tree */ + wf_fcu_lookup_fans(pv); + + /* + * Older machines don't have the device-tree entries + * we are looking for, just hard code the list + */ + if (list_empty(&pv->fan_list)) + wf_fcu_default_fans(pv); + + /* Still no fans ? FAIL */ + if (list_empty(&pv->fan_list)) { + pr_err("wf_fcu: Failed to find fans for your machine\n"); + kfree(pv); + return -ENODEV; + } + + dev_set_drvdata(&client->dev, pv); + + return 0; +} + +static int wf_fcu_remove(struct i2c_client *client) +{ + struct wf_fcu_priv *pv = dev_get_drvdata(&client->dev); + struct wf_fcu_fan *fan; + + while (!list_empty(&pv->fan_list)) { + fan = list_first_entry(&pv->fan_list, struct wf_fcu_fan, link); + list_del(&fan->link); + wf_unregister_control(&fan->ctrl); + } + kref_put(&pv->ref, wf_fcu_release); + return 0; +} + +static const struct i2c_device_id wf_fcu_id[] = { + { "MAC,fcu", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wf_fcu_id); + +static struct i2c_driver wf_fcu_driver = { + .driver = { + .name = "wf_fcu", + }, + .probe = wf_fcu_probe, + .remove = wf_fcu_remove, + .id_table = wf_fcu_id, +}; + +module_i2c_driver(wf_fcu_driver); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("FCU control objects for PowerMacs thermal control"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 647c6add219..590214ba736 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -18,13 +18,12 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/pmac_low_i2c.h> #include "windfarm.h" -#define VERSION "0.2" +#define VERSION "1.0" #undef DEBUG @@ -37,8 +36,8 @@ struct wf_lm75_sensor { int ds1775 : 1; int inited : 1; - struct i2c_client *i2c; - struct wf_sensor sens; + struct i2c_client *i2c; + struct wf_sensor sens; }; #define wf_to_lm75(c) container_of(c, struct wf_lm75_sensor, sens) @@ -91,40 +90,19 @@ static struct wf_sensor_ops wf_lm75_ops = { static int wf_lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ +{ struct wf_lm75_sensor *lm; - int rc; - - lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); - if (lm == NULL) - return -ENODEV; - - lm->inited = 0; - lm->ds1775 = id->driver_data; - lm->i2c = client; - lm->sens.name = client->dev.platform_data; - lm->sens.ops = &wf_lm75_ops; - i2c_set_clientdata(client, lm); - - rc = wf_register_sensor(&lm->sens); - if (rc) - kfree(lm); - - return rc; -} - -static struct i2c_driver wf_lm75_driver; - -static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, - u8 addr, int ds1775, - const char *loc) -{ - struct i2c_board_info info; - struct i2c_client *client; - char *name; + int rc, ds1775 = id->driver_data; + const char *name, *loc; DBG("wf_lm75: creating %s device at address 0x%02x\n", - ds1775 ? "ds1775" : "lm75", addr); + ds1775 ? "ds1775" : "lm75", client->addr); + + loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); + if (!loc) { + dev_warn(&client->dev, "Missing hwsensor-location property!\n"); + return -ENXIO; + } /* Usual rant about sensor names not beeing very consistent in * the device-tree, oh well ... @@ -138,68 +116,31 @@ static struct i2c_client *wf_lm75_create(struct i2c_adapter *adapter, name = "optical-drive-temp"; else if (!strcmp(loc, "HD Temp")) name = "hard-drive-temp"; + else if (!strcmp(loc, "PCI SLOTS")) + name = "slots-temp"; + else if (!strcmp(loc, "CPU A INLET")) + name = "cpu-inlet-temp-0"; + else if (!strcmp(loc, "CPU B INLET")) + name = "cpu-inlet-temp-1"; else - goto fail; - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = (addr >> 1) & 0x7f; - info.platform_data = name; - strlcpy(info.type, ds1775 ? "wf_ds1775" : "wf_lm75", I2C_NAME_SIZE); - - client = i2c_new_device(adapter, &info); - if (client == NULL) { - printk(KERN_ERR "windfarm: failed to attach %s %s to i2c\n", - ds1775 ? "ds1775" : "lm75", name); - goto fail; - } - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &wf_lm75_driver.clients); - return client; - fail: - return NULL; -} + return -ENXIO; + -static int wf_lm75_attach(struct i2c_adapter *adapter) -{ - struct device_node *busnode, *dev; - struct pmac_i2c_bus *bus; - - DBG("wf_lm75: adapter %s detected\n", adapter->name); - - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) + lm = kzalloc(sizeof(struct wf_lm75_sensor), GFP_KERNEL); + if (lm == NULL) return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - - DBG("wf_lm75: bus found, looking for device...\n"); - /* Now look for lm75(s) in there */ - for (dev = NULL; - (dev = of_get_next_child(busnode, dev)) != NULL;) { - const char *loc = - of_get_property(dev, "hwsensor-location", NULL); - u8 addr; + lm->inited = 0; + lm->ds1775 = ds1775; + lm->i2c = client; + lm->sens.name = name; + lm->sens.ops = &wf_lm75_ops; + i2c_set_clientdata(client, lm); - /* We must re-match the adapter in order to properly check - * the channel on multibus setups - */ - if (!pmac_i2c_match_adapter(dev, adapter)) - continue; - addr = pmac_i2c_get_dev_addr(dev); - if (loc == NULL || addr == 0) - continue; - /* real lm75 */ - if (of_device_is_compatible(dev, "lm75")) - wf_lm75_create(adapter, addr, 0, loc); - /* ds1775 (compatible, better resolution */ - else if (of_device_is_compatible(dev, "ds1775")) - wf_lm75_create(adapter, addr, 1, loc); - } - return 0; + rc = wf_register_sensor(&lm->sens); + if (rc) + kfree(lm); + return rc; } static int wf_lm75_remove(struct i2c_client *client) @@ -218,39 +159,22 @@ static int wf_lm75_remove(struct i2c_client *client) } static const struct i2c_device_id wf_lm75_id[] = { - { "wf_lm75", 0 }, - { "wf_ds1775", 1 }, + { "MAC,lm75", 0 }, + { "MAC,ds1775", 1 }, { } }; +MODULE_DEVICE_TABLE(i2c, wf_lm75_id); static struct i2c_driver wf_lm75_driver = { .driver = { .name = "wf_lm75", }, - .attach_adapter = wf_lm75_attach, .probe = wf_lm75_probe, .remove = wf_lm75_remove, .id_table = wf_lm75_id, }; -static int __init wf_lm75_sensor_init(void) -{ - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; - return i2c_add_driver(&wf_lm75_driver); -} - -static void __exit wf_lm75_sensor_exit(void) -{ - i2c_del_driver(&wf_lm75_driver); -} - - -module_init(wf_lm75_sensor_init); -module_exit(wf_lm75_sensor_exit); +module_i2c_driver(wf_lm75_driver); MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 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 new file mode 100644 index 00000000000..c071aab79dd --- /dev/null +++ b/drivers/macintosh/windfarm_lm87_sensor.c @@ -0,0 +1,201 @@ +/* + * Windfarm PowerMac thermal control. LM87 sensor + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + * + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/i2c.h> +#include <asm/prom.h> +#include <asm/machdep.h> +#include <asm/io.h> +#include <asm/sections.h> +#include <asm/pmac_low_i2c.h> + +#include "windfarm.h" + +#define VERSION "1.0" + +#undef DEBUG + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +struct wf_lm87_sensor { + struct i2c_client *i2c; + struct wf_sensor sens; +}; +#define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens) + + +static int wf_lm87_read_reg(struct i2c_client *chip, int reg) +{ + int rc, tries = 0; + u8 buf; + + for (;;) { + /* Set address */ + buf = (u8)reg; + rc = i2c_master_send(chip, &buf, 1); + if (rc <= 0) + goto error; + rc = i2c_master_recv(chip, &buf, 1); + if (rc <= 0) + goto error; + return (int)buf; + error: + DBG("wf_lm87: Error reading LM87, retrying...\n"); + if (++tries > 10) { + printk(KERN_ERR "wf_lm87: Error reading LM87 !\n"); + return -EIO; + } + msleep(10); + } +} + +static int wf_lm87_get(struct wf_sensor *sr, s32 *value) +{ + struct wf_lm87_sensor *lm = sr->priv; + s32 temp; + + if (lm->i2c == NULL) + return -ENODEV; + +#define LM87_INT_TEMP 0x27 + + /* Read temperature register */ + temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP); + if (temp < 0) + return temp; + *value = temp << 16; + + return 0; +} + +static void wf_lm87_release(struct wf_sensor *sr) +{ + struct wf_lm87_sensor *lm = wf_to_lm87(sr); + + kfree(lm); +} + +static struct wf_sensor_ops wf_lm87_ops = { + .get_value = wf_lm87_get, + .release = wf_lm87_release, + .owner = THIS_MODULE, +}; + +static int wf_lm87_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wf_lm87_sensor *lm; + const char *name = NULL, *loc; + struct device_node *np = NULL; + int rc; + + /* + * The lm87 contains a whole pile of sensors, additionally, + * the Xserve G5 has several lm87's. However, for now we only + * care about the internal temperature sensor + */ + while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) { + if (strcmp(np->name, "int-temp")) + continue; + loc = of_get_property(np, "location", NULL); + if (!loc) + continue; + if (strstr(loc, "DIMM")) + name = "dimms-temp"; + else if (strstr(loc, "Processors")) + name = "between-cpus-temp"; + if (name) { + of_node_put(np); + break; + } + } + if (!name) { + pr_warning("wf_lm87: Unsupported sensor %s\n", + client->dev.of_node->full_name); + return -ENODEV; + } + + lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL); + if (lm == NULL) + return -ENODEV; + + lm->i2c = client; + lm->sens.name = name; + lm->sens.ops = &wf_lm87_ops; + lm->sens.priv = lm; + i2c_set_clientdata(client, lm); + + rc = wf_register_sensor(&lm->sens); + if (rc) + kfree(lm); + return rc; +} + +static int wf_lm87_remove(struct i2c_client *client) +{ + struct wf_lm87_sensor *lm = i2c_get_clientdata(client); + + DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name); + + /* Mark client detached */ + lm->i2c = NULL; + + /* release sensor */ + wf_unregister_sensor(&lm->sens); + + return 0; +} + +static const struct i2c_device_id wf_lm87_id[] = { + { "MAC,lm87cimt", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wf_lm87_id); + +static struct i2c_driver wf_lm87_driver = { + .driver = { + .name = "wf_lm87", + }, + .probe = wf_lm87_probe, + .remove = wf_lm87_remove, + .id_table = wf_lm87_id, +}; + +static int __init wf_lm87_sensor_init(void) +{ + /* We only support this on the Xserve */ + if (!of_machine_is_compatible("RackMac3,1")) + return -ENODEV; + + return i2c_add_driver(&wf_lm87_driver); +} + +static void __exit wf_lm87_sensor_exit(void) +{ + i2c_del_driver(&wf_lm87_driver); +} + + +module_init(wf_lm87_sensor_init); +module_exit(wf_lm87_sensor_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index 8204113268f..87e439b1031 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c @@ -16,7 +16,7 @@ #include "windfarm.h" -#define VERSION "0.2" +#define VERSION "1.0" /* This currently only exports the external temperature sensor, since that's all the control loops need. */ @@ -64,9 +64,29 @@ static struct wf_sensor_ops wf_max6690_ops = { static int wf_max6690_probe(struct i2c_client *client, const struct i2c_device_id *id) { + const char *name, *loc; struct wf_6690_sensor *max; int rc; + loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); + if (!loc) { + dev_warn(&client->dev, "Missing hwsensor-location property!\n"); + return -ENXIO; + } + + /* + * We only expose the external temperature register for + * now as this is all we need for our control loops + */ + if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT")) + name = "backside-temp"; + else if (!strcmp(loc, "NB Ambient")) + name = "north-bridge-temp"; + else if (!strcmp(loc, "GPU Ambient")) + name = "gpu-temp"; + else + return -ENXIO; + max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); if (max == NULL) { printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " @@ -75,90 +95,16 @@ static int wf_max6690_probe(struct i2c_client *client, } max->i2c = client; - max->sens.name = client->dev.platform_data; + max->sens.name = name; max->sens.ops = &wf_max6690_ops; i2c_set_clientdata(client, max); rc = wf_register_sensor(&max->sens); - if (rc) { + if (rc) kfree(max); - } - return rc; } -static struct i2c_driver wf_max6690_driver; - -static struct i2c_client *wf_max6690_create(struct i2c_adapter *adapter, - u8 addr, const char *loc) -{ - struct i2c_board_info info; - struct i2c_client *client; - char *name; - - if (!strcmp(loc, "BACKSIDE")) - name = "backside-temp"; - else if (!strcmp(loc, "NB Ambient")) - name = "north-bridge-temp"; - else if (!strcmp(loc, "GPU Ambient")) - name = "gpu-temp"; - else - goto fail; - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = addr >> 1; - info.platform_data = name; - strlcpy(info.type, "wf_max6690", I2C_NAME_SIZE); - - client = i2c_new_device(adapter, &info); - if (client == NULL) { - printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n"); - goto fail; - } - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &wf_max6690_driver.clients); - return client; - - fail: - return NULL; -} - -static int wf_max6690_attach(struct i2c_adapter *adapter) -{ - struct device_node *busnode, *dev = NULL; - struct pmac_i2c_bus *bus; - const char *loc; - - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) - return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - - while ((dev = of_get_next_child(busnode, dev)) != NULL) { - u8 addr; - - /* We must re-match the adapter in order to properly check - * the channel on multibus setups - */ - if (!pmac_i2c_match_adapter(dev, adapter)) - continue; - if (!of_device_is_compatible(dev, "max6690")) - continue; - addr = pmac_i2c_get_dev_addr(dev); - loc = of_get_property(dev, "hwsensor-location", NULL); - if (loc == NULL || addr == 0) - continue; - printk("found max6690, loc=%s addr=0x%02x\n", loc, addr); - wf_max6690_create(adapter, addr, loc); - } - - return 0; -} - static int wf_max6690_remove(struct i2c_client *client) { struct wf_6690_sensor *max = i2c_get_clientdata(client); @@ -170,37 +116,21 @@ static int wf_max6690_remove(struct i2c_client *client) } static const struct i2c_device_id wf_max6690_id[] = { - { "wf_max6690", 0 }, + { "MAC,max6690", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, wf_max6690_id); static struct i2c_driver wf_max6690_driver = { .driver = { .name = "wf_max6690", }, - .attach_adapter = wf_max6690_attach, .probe = wf_max6690_probe, .remove = wf_max6690_remove, .id_table = wf_max6690_id, }; -static int __init wf_max6690_sensor_init(void) -{ - /* Don't register on old machines that use therm_pm72 for now */ - if (of_machine_is_compatible("PowerMac7,2") || - of_machine_is_compatible("PowerMac7,3") || - of_machine_is_compatible("RackMac3,1")) - return -ENODEV; - return i2c_add_driver(&wf_max6690_driver); -} - -static void __exit wf_max6690_sensor_exit(void) -{ - i2c_del_driver(&wf_max6690_driver); -} - -module_init(wf_max6690_sensor_init); -module_exit(wf_max6690_sensor_exit); +module_i2c_driver(wf_max6690_driver); MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); MODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control"); diff --git a/drivers/macintosh/windfarm_mpu.h b/drivers/macintosh/windfarm_mpu.h new file mode 100644 index 00000000000..046edc8c2ec --- /dev/null +++ b/drivers/macintosh/windfarm_mpu.h @@ -0,0 +1,105 @@ +/* + * Windfarm PowerMac thermal control + * + * Copyright 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Released under the term of the GNU GPL v2. + */ + +#ifndef __WINDFARM_MPU_H +#define __WINDFARM_MPU_H + +typedef unsigned short fu16; +typedef int fs32; +typedef short fs16; + +/* Definition of the MPU data structure which contains per CPU + * calibration information (among others) for the G5 machines + */ +struct mpu_data +{ + u8 signature; /* 0x00 - EEPROM sig. */ + u8 bytes_used; /* 0x01 - Bytes used in eeprom (160 ?) */ + u8 size; /* 0x02 - EEPROM size (256 ?) */ + u8 version; /* 0x03 - EEPROM version */ + u32 data_revision; /* 0x04 - Dataset revision */ + u8 processor_bin_code[3]; /* 0x08 - Processor BIN code */ + u8 bin_code_expansion; /* 0x0b - ??? (padding ?) */ + u8 processor_num; /* 0x0c - Number of CPUs on this MPU */ + u8 input_mul_bus_div; /* 0x0d - Clock input multiplier/bus divider */ + u8 reserved1[2]; /* 0x0e - */ + u32 input_clk_freq_high; /* 0x10 - Input clock frequency high */ + u8 cpu_nb_target_cycles; /* 0x14 - ??? */ + u8 cpu_statlat; /* 0x15 - ??? */ + u8 cpu_snooplat; /* 0x16 - ??? */ + u8 cpu_snoopacc; /* 0x17 - ??? */ + u8 nb_paamwin; /* 0x18 - ??? */ + u8 nb_statlat; /* 0x19 - ??? */ + u8 nb_snooplat; /* 0x1a - ??? */ + u8 nb_snoopwin; /* 0x1b - ??? */ + u8 api_bus_mode; /* 0x1c - ??? */ + u8 reserved2[3]; /* 0x1d - */ + u32 input_clk_freq_low; /* 0x20 - Input clock frequency low */ + u8 processor_card_slot; /* 0x24 - Processor card slot number */ + u8 reserved3[2]; /* 0x25 - */ + u8 padjmax; /* 0x27 - Max power adjustment (Not in OF!) */ + u8 ttarget; /* 0x28 - Target temperature */ + u8 tmax; /* 0x29 - Max temperature */ + u8 pmaxh; /* 0x2a - Max power */ + u8 tguardband; /* 0x2b - Guardband temp ??? Hist. len in OSX */ + fs32 pid_gp; /* 0x2c - PID proportional gain */ + fs32 pid_gr; /* 0x30 - PID reset gain */ + fs32 pid_gd; /* 0x34 - PID derivative gain */ + fu16 voph; /* 0x38 - Vop High */ + fu16 vopl; /* 0x3a - Vop Low */ + fs16 nactual_die; /* 0x3c - nActual Die */ + fs16 nactual_heatsink; /* 0x3e - nActual Heatsink */ + fs16 nactual_system; /* 0x40 - nActual System */ + u16 calibration_flags; /* 0x42 - Calibration flags */ + fu16 mdiode; /* 0x44 - Diode M value (scaling factor) */ + fs16 bdiode; /* 0x46 - Diode B value (offset) */ + fs32 theta_heat_sink; /* 0x48 - Theta heat sink */ + u16 rminn_intake_fan; /* 0x4c - Intake fan min RPM */ + u16 rmaxn_intake_fan; /* 0x4e - Intake fan max RPM */ + u16 rminn_exhaust_fan; /* 0x50 - Exhaust fan min RPM */ + u16 rmaxn_exhaust_fan; /* 0x52 - Exhaust fan max RPM */ + u8 processor_part_num[8]; /* 0x54 - Processor part number XX pumps min/max */ + u32 processor_lot_num; /* 0x5c - Processor lot number */ + u8 orig_card_sernum[0x10]; /* 0x60 - Card original serial number */ + u8 curr_card_sernum[0x10]; /* 0x70 - Card current serial number */ + u8 mlb_sernum[0x18]; /* 0x80 - MLB serial number */ + u32 checksum1; /* 0x98 - */ + u32 checksum2; /* 0x9c - */ +}; /* Total size = 0xa0 */ + +static inline const struct mpu_data *wf_get_mpu(int cpu) +{ + struct device_node *np; + char nodename[64]; + const void *data; + int len; + + /* + * prom.c routine for finding a node by path is a bit brain dead + * and requires exact @xxx unit numbers. This is a bit ugly but + * will work for these machines + */ + sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0); + np = of_find_node_by_path(nodename); + if (!np) + return NULL; + data = of_get_property(np, "cpuid", &len); + of_node_put(np); + if (!data) + return NULL; + + /* + * We are naughty, we have dropped the reference to the device + * node and still return a pointer to the content. We know we + * can do that though as this is only ever called on PowerMac + * which cannot remove those nodes + */ + return data; +} + +#endif /* __WINDFARM_MPU_H */ diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c index e0ee80700cd..3024685e4cc 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) return 0; } -static int __devexit wf_pm112_remove(struct platform_device *dev) +static int wf_pm112_remove(struct platform_device *dev) { wf_unregister_client(&pm112_events); /* should release all sensors and controls */ @@ -665,7 +665,7 @@ static int __devexit wf_pm112_remove(struct platform_device *dev) static struct platform_driver wf_pm112_driver = { .probe = wf_pm112_probe, - .remove = __devexit_p(wf_pm112_remove), + .remove = wf_pm112_remove, .driver = { .name = "windfarm", .owner = THIS_MODULE, @@ -681,7 +681,7 @@ static int __init wf_pm112_init(void) /* Count the number of CPU cores */ nr_cores = 0; - for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) + for_each_node_by_type(cpu, "cpu") ++nr_cores; printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n"); diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c index 30e6195e19d..b350fb86ff0 100644 --- a/drivers/macintosh/windfarm_pm121.c +++ b/drivers/macintosh/windfarm_pm121.c @@ -215,7 +215,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/smu.h> @@ -277,6 +276,7 @@ static const char *loop_names[N_LOOPS] = { static unsigned int pm121_failure_state; static int pm121_readjust, pm121_skipping; +static bool pm121_overtemp; static s32 average_power; struct pm121_correction { @@ -555,8 +555,18 @@ static void pm121_create_sys_fans(int loop_id) pid_param.interval = PM121_SYS_INTERVAL; pid_param.history_len = PM121_SYS_HISTORY_SIZE; pid_param.itarget = param->itarget; - pid_param.min = control->ops->get_min(control); - pid_param.max = control->ops->get_max(control); + if(control) + { + pid_param.min = control->ops->get_min(control); + pid_param.max = control->ops->get_max(control); + } else { + /* + * This is probably not the right!? + * Perhaps goto fail if control == NULL above? + */ + pid_param.min = 0; + pid_param.max = 0; + } wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param); @@ -571,7 +581,7 @@ static void pm121_create_sys_fans(int loop_id) control the same control */ printk(KERN_WARNING "pm121: failed to set up %s loop " "setting \"%s\" to max speed.\n", - loop_names[loop_id], control->name); + loop_names[loop_id], control ? control->name : "uninitialized value"); if (control) wf_control_set_max(control); @@ -848,6 +858,7 @@ static void pm121_tick(void) if (new_failure & FAILURE_OVERTEMP) { wf_set_overtemp(); pm121_skipping = 2; + pm121_overtemp = true; } /* We only clear the overtemp condition if overtemp is cleared @@ -856,8 +867,10 @@ static void pm121_tick(void) * the control loop levels, but we don't want to keep it clear * here in this case */ - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP) + if (!pm121_failure_state && pm121_overtemp) { wf_clear_overtemp(); + pm121_overtemp = false; + } } @@ -988,7 +1001,7 @@ static int pm121_probe(struct platform_device *ddev) return 0; } -static int __devexit pm121_remove(struct platform_device *ddev) +static int pm121_remove(struct platform_device *ddev) { wf_unregister_client(&pm121_events); return 0; @@ -996,7 +1009,7 @@ static int __devexit pm121_remove(struct platform_device *ddev) static struct platform_driver pm121_driver = { .probe = pm121_probe, - .remove = __devexit_p(pm121_remove), + .remove = pm121_remove, .driver = { .name = "windfarm", .bus = &platform_bus_type, diff --git a/drivers/macintosh/windfarm_pm72.c b/drivers/macintosh/windfarm_pm72.c new file mode 100644 index 00000000000..2f506b9d5a5 --- /dev/null +++ b/drivers/macintosh/windfarm_pm72.c @@ -0,0 +1,847 @@ +/* + * Windfarm PowerMac thermal control. + * Control loops for PowerMac7,2 and 7,3 + * + * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Use and redistribute under the terms of the GNU GPL v2. + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <asm/prom.h> +#include <asm/smu.h> + +#include "windfarm.h" +#include "windfarm_pid.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +#undef DEBUG +#undef LOTSA_DEBUG + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +#ifdef LOTSA_DEBUG +#define DBG_LOTS(args...) printk(args) +#else +#define DBG_LOTS(args...) do { } while(0) +#endif + +/* define this to force CPU overtemp to 60 degree, useful for testing + * the overtemp code + */ +#undef HACKED_OVERTEMP + +/* We currently only handle 2 chips */ +#define NR_CHIPS 2 +#define NR_CPU_FANS 3 * NR_CHIPS + +/* Controls and sensors */ +static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; +static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; +static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; +static struct wf_sensor *backside_temp; +static struct wf_sensor *drives_temp; + +static struct wf_control *cpu_front_fans[NR_CHIPS]; +static struct wf_control *cpu_rear_fans[NR_CHIPS]; +static struct wf_control *cpu_pumps[NR_CHIPS]; +static struct wf_control *backside_fan; +static struct wf_control *drives_fan; +static struct wf_control *slots_fan; +static struct wf_control *cpufreq_clamp; + +/* We keep a temperature history for average calculation of 180s */ +#define CPU_TEMP_HIST_SIZE 180 + +/* Fixed speed for slot fan */ +#define SLOTS_FAN_DEFAULT_PWM 40 + +/* Scale value for CPU intake fans */ +#define CPU_INTAKE_SCALE 0x0000f852 + +/* PID loop state */ +static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; +static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; +static bool cpu_pid_combined; +static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; +static int cpu_thist_pt; +static s64 cpu_thist_total; +static s32 cpu_all_tmax = 100 << 16; +static struct wf_pid_state backside_pid; +static int backside_tick; +static struct wf_pid_state drives_pid; +static int drives_tick; + +static int nr_chips; +static bool have_all_controls; +static bool have_all_sensors; +static bool started; + +static int failure_state; +#define FAILURE_SENSOR 1 +#define FAILURE_FAN 2 +#define FAILURE_PERM 4 +#define FAILURE_LOW_OVERTEMP 8 +#define FAILURE_HIGH_OVERTEMP 16 + +/* Overtemp values */ +#define LOW_OVER_AVERAGE 0 +#define LOW_OVER_IMMEDIATE (10 << 16) +#define LOW_OVER_CLEAR ((-10) << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) +#define HIGH_OVER_AVERAGE (10 << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) + + +static void cpu_max_all_fans(void) +{ + int i; + + /* We max all CPU fans in case of a sensor error. We also do the + * cpufreq clamping now, even if it's supposedly done later by the + * generic code anyway, we do it earlier here to react faster + */ + if (cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + for (i = 0; i < nr_chips; i++) { + if (cpu_front_fans[i]) + wf_control_set_max(cpu_front_fans[i]); + if (cpu_rear_fans[i]) + wf_control_set_max(cpu_rear_fans[i]); + if (cpu_pumps[i]) + wf_control_set_max(cpu_pumps[i]); + } +} + +static int cpu_check_overtemp(s32 temp) +{ + int new_state = 0; + s32 t_avg, t_old; + static bool first = true; + + /* First check for immediate overtemps */ + if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" + " temperature !\n"); + } + if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " immediate CPU temperature !\n"); + } + + /* + * The first time around, initialize the array with the first + * temperature reading + */ + if (first) { + int i; + + cpu_thist_total = 0; + for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { + cpu_thist[i] = temp; + cpu_thist_total += temp; + } + first = false; + } + + /* + * We calculate a history of max temperatures and use that for the + * overtemp management + */ + t_old = cpu_thist[cpu_thist_pt]; + cpu_thist[cpu_thist_pt] = temp; + cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; + cpu_thist_total -= t_old; + cpu_thist_total += temp; + t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; + + DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", + FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); + + /* Now check for average overtemps */ + if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to average CPU" + " temperature !\n"); + } + if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " average CPU temperature !\n"); + } + + /* Now handle overtemp conditions. We don't currently use the windfarm + * overtemp handling core as it's not fully suited to the needs of those + * new machine. This will be fixed later. + */ + if (new_state) { + /* High overtemp -> immediate shutdown */ + if (new_state & FAILURE_HIGH_OVERTEMP) + machine_power_off(); + if ((failure_state & new_state) != new_state) + cpu_max_all_fans(); + failure_state |= new_state; + } else if ((failure_state & FAILURE_LOW_OVERTEMP) && + (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { + printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); + failure_state &= ~FAILURE_LOW_OVERTEMP; + } + + return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); +} + +static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) +{ + s32 dtemp, volts, amps; + int rc; + + /* Get diode temperature */ + rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); + if (rc) { + DBG(" CPU%d: temp reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); + *temp = dtemp; + + /* Get voltage */ + rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); + if (rc) { + DBG(" CPU%d, volts reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); + + /* Get current */ + rc = wf_sensor_get(sens_cpu_amps[cpu], &s); + if (rc) { + DBG(" CPU%d, current reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); + + /* Calculate power */ + + /* Scale voltage and current raw sensor values according to fixed scales + * obtained in Darwin and calculate power from I and V + */ + *power = (((u64)volts) * ((u64)amps)) >> 16; + + DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); + + return 0; + +} + +static void cpu_fans_tick_split(void) +{ + int err, cpu; + s32 intake, temp, power, t_max = 0; + + DBG_LOTS("* cpu fans_tick_split()\n"); + + for (cpu = 0; cpu < nr_chips; ++cpu) { + struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; + + /* Read current speed */ + wf_control_get(cpu_rear_fans[cpu], &sp->target); + + DBG_LOTS(" CPU%d: cur_target = %d RPM\n", cpu, sp->target); + + err = read_one_cpu_vals(cpu, &temp, &power); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + + /* Keep track of highest temp */ + t_max = max(t_max, temp); + + /* Handle possible overtemps */ + if (cpu_check_overtemp(t_max)) + return; + + /* Run PID */ + wf_cpu_pid_run(sp, power, temp); + + DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); + + /* Apply result directly to exhaust fan */ + err = wf_control_set(cpu_rear_fans[cpu], sp->target); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_rear_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + break; + } + + /* Scale result for intake fan */ + intake = (sp->target * CPU_INTAKE_SCALE) >> 16; + DBG_LOTS(" CPU%d: intake = %d RPM\n", cpu, intake); + err = wf_control_set(cpu_front_fans[cpu], intake); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_front_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + break; + } + } +} + +static void cpu_fans_tick_combined(void) +{ + s32 temp0, power0, temp1, power1, t_max = 0; + s32 temp, power, intake, pump; + struct wf_control *pump0, *pump1; + struct wf_cpu_pid_state *sp = &cpu_pid[0]; + int err, cpu; + + DBG_LOTS("* cpu fans_tick_combined()\n"); + + /* Read current speed from cpu 0 */ + wf_control_get(cpu_rear_fans[0], &sp->target); + + DBG_LOTS(" CPUs: cur_target = %d RPM\n", sp->target); + + /* Read values for both CPUs */ + err = read_one_cpu_vals(0, &temp0, &power0); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + err = read_one_cpu_vals(1, &temp1, &power1); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + + /* Keep track of highest temp */ + t_max = max(t_max, max(temp0, temp1)); + + /* Handle possible overtemps */ + if (cpu_check_overtemp(t_max)) + return; + + /* Use the max temp & power of both */ + temp = max(temp0, temp1); + power = max(power0, power1); + + /* Run PID */ + wf_cpu_pid_run(sp, power, temp); + + /* Scale result for intake fan */ + intake = (sp->target * CPU_INTAKE_SCALE) >> 16; + + /* Same deal with pump speed */ + pump0 = cpu_pumps[0]; + pump1 = cpu_pumps[1]; + if (!pump0) { + pump0 = pump1; + pump1 = NULL; + } + pump = (sp->target * wf_control_get_max(pump0)) / + cpu_mpu_data[0]->rmaxn_exhaust_fan; + + DBG_LOTS(" CPUs: target = %d RPM\n", sp->target); + DBG_LOTS(" CPUs: intake = %d RPM\n", intake); + DBG_LOTS(" CPUs: pump = %d RPM\n", pump); + + for (cpu = 0; cpu < nr_chips; cpu++) { + err = wf_control_set(cpu_rear_fans[cpu], sp->target); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_rear_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + } + err = wf_control_set(cpu_front_fans[cpu], intake); + if (err) { + pr_warning("wf_pm72: Fan %s reports error %d\n", + cpu_front_fans[cpu]->name, err); + failure_state |= FAILURE_FAN; + } + err = 0; + if (cpu_pumps[cpu]) + err = wf_control_set(cpu_pumps[cpu], pump); + if (err) { + pr_warning("wf_pm72: Pump %s reports error %d\n", + cpu_pumps[cpu]->name, err); + failure_state |= FAILURE_FAN; + } + } +} + +/* Implementation... */ +static int cpu_setup_pid(int cpu) +{ + struct wf_cpu_pid_param pid; + const struct mpu_data *mpu = cpu_mpu_data[cpu]; + s32 tmax, ttarget, ptarget; + int fmin, fmax, hsize; + + /* Get PID params from the appropriate MPU EEPROM */ + tmax = mpu->tmax << 16; + ttarget = mpu->ttarget << 16; + ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; + + DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", + cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); + + /* We keep a global tmax for overtemp calculations */ + if (tmax < cpu_all_tmax) + cpu_all_tmax = tmax; + + /* Set PID min/max by using the rear fan min/max */ + fmin = wf_control_get_min(cpu_rear_fans[cpu]); + fmax = wf_control_get_max(cpu_rear_fans[cpu]); + DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); + + /* History size */ + hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); + DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); + + /* Initialize PID loop */ + pid.interval = 1; /* seconds */ + pid.history_len = hsize; + pid.gd = mpu->pid_gd; + pid.gp = mpu->pid_gp; + pid.gr = mpu->pid_gr; + pid.tmax = tmax; + pid.ttarget = ttarget; + pid.pmaxadj = ptarget; + pid.min = fmin; + pid.max = fmax; + + wf_cpu_pid_init(&cpu_pid[cpu], &pid); + cpu_pid[cpu].target = 1000; + + return 0; +} + +/* Backside/U3 fan */ +static struct wf_pid_param backside_u3_param = { + .interval = 5, + .history_len = 2, + .gd = 40 << 20, + .gp = 5 << 20, + .gr = 0, + .itarget = 65 << 16, + .additive = 1, + .min = 20, + .max = 100, +}; + +static struct wf_pid_param backside_u3h_param = { + .interval = 5, + .history_len = 2, + .gd = 20 << 20, + .gp = 5 << 20, + .gr = 0, + .itarget = 75 << 16, + .additive = 1, + .min = 20, + .max = 100, +}; + +static void backside_fan_tick(void) +{ + s32 temp; + int speed; + int err; + + if (!backside_fan || !backside_temp || !backside_tick) + return; + if (--backside_tick > 0) + return; + backside_tick = backside_pid.param.interval; + + DBG_LOTS("* backside fans tick\n"); + + /* Update fan speed from actual fans */ + err = wf_control_get(backside_fan, &speed); + if (!err) + backside_pid.target = speed; + + err = wf_sensor_get(backside_temp, &temp); + if (err) { + printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n", + err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(backside_fan); + return; + } + speed = wf_pid_run(&backside_pid, temp); + + DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + err = wf_control_set(backside_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: backside fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void backside_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(backside_fan); + s32 fmax = wf_control_get_max(backside_fan); + struct wf_pid_param param; + struct device_node *u3; + int u3h = 1; /* conservative by default */ + + u3 = of_find_node_by_path("/u3@0,f8000000"); + if (u3 != NULL) { + const u32 *vers = of_get_property(u3, "device-rev", NULL); + if (vers) + if (((*vers) & 0x3f) < 0x34) + u3h = 0; + of_node_put(u3); + } + + param = u3h ? backside_u3h_param : backside_u3_param; + + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&backside_pid, ¶m); + backside_tick = 1; + + pr_info("wf_pm72: Backside control loop started.\n"); +} + +/* Drive bay fan */ +static const struct wf_pid_param drives_param = { + .interval = 5, + .history_len = 2, + .gd = 30 << 20, + .gp = 5 << 20, + .gr = 0, + .itarget = 40 << 16, + .additive = 1, + .min = 300, + .max = 4000, +}; + +static void drives_fan_tick(void) +{ + s32 temp; + int speed; + int err; + + if (!drives_fan || !drives_temp || !drives_tick) + return; + if (--drives_tick > 0) + return; + drives_tick = drives_pid.param.interval; + + DBG_LOTS("* drives fans tick\n"); + + /* Update fan speed from actual fans */ + err = wf_control_get(drives_fan, &speed); + if (!err) + drives_pid.target = speed; + + err = wf_sensor_get(drives_temp, &temp); + if (err) { + pr_warning("wf_pm72: drive bay temp sensor error %d\n", err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(drives_fan); + return; + } + speed = wf_pid_run(&drives_pid, temp); + + DBG_LOTS("drives PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + err = wf_control_set(drives_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void drives_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(drives_fan); + s32 fmax = wf_control_get_max(drives_fan); + struct wf_pid_param param = drives_param; + + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&drives_pid, ¶m); + drives_tick = 1; + + pr_info("wf_pm72: Drive bay control loop started.\n"); +} + +static void set_fail_state(void) +{ + cpu_max_all_fans(); + + if (backside_fan) + wf_control_set_max(backside_fan); + if (slots_fan) + wf_control_set_max(slots_fan); + if (drives_fan) + wf_control_set_max(drives_fan); +} + +static void pm72_tick(void) +{ + int i, last_failure; + + if (!started) { + started = 1; + printk(KERN_INFO "windfarm: CPUs control loops started.\n"); + for (i = 0; i < nr_chips; ++i) { + if (cpu_setup_pid(i) < 0) { + failure_state = FAILURE_PERM; + set_fail_state(); + break; + } + } + DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); + + backside_setup_pid(); + drives_setup_pid(); + + /* + * We don't have the right stuff to drive the PCI fan + * so we fix it to a default value + */ + wf_control_set(slots_fan, SLOTS_FAN_DEFAULT_PWM); + +#ifdef HACKED_OVERTEMP + cpu_all_tmax = 60 << 16; +#endif + } + + /* Permanent failure, bail out */ + if (failure_state & FAILURE_PERM) + return; + + /* + * Clear all failure bits except low overtemp which will be eventually + * cleared by the control loop itself + */ + last_failure = failure_state; + failure_state &= FAILURE_LOW_OVERTEMP; + if (cpu_pid_combined) + cpu_fans_tick_combined(); + else + cpu_fans_tick_split(); + backside_fan_tick(); + drives_fan_tick(); + + DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", + last_failure, failure_state); + + /* Check for failures. Any failure causes cpufreq clamping */ + if (failure_state && last_failure == 0 && cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + if (failure_state == 0 && last_failure && cpufreq_clamp) + wf_control_set_min(cpufreq_clamp); + + /* That's it for now, we might want to deal with other failures + * differently in the future though + */ +} + +static void pm72_new_control(struct wf_control *ct) +{ + bool all_controls; + bool had_pump = cpu_pumps[0] || cpu_pumps[1]; + + if (!strcmp(ct->name, "cpu-front-fan-0")) + cpu_front_fans[0] = ct; + else if (!strcmp(ct->name, "cpu-front-fan-1")) + cpu_front_fans[1] = ct; + else if (!strcmp(ct->name, "cpu-rear-fan-0")) + cpu_rear_fans[0] = ct; + else if (!strcmp(ct->name, "cpu-rear-fan-1")) + cpu_rear_fans[1] = ct; + else if (!strcmp(ct->name, "cpu-pump-0")) + cpu_pumps[0] = ct; + else if (!strcmp(ct->name, "cpu-pump-1")) + cpu_pumps[1] = ct; + else if (!strcmp(ct->name, "backside-fan")) + backside_fan = ct; + else if (!strcmp(ct->name, "slots-fan")) + slots_fan = ct; + else if (!strcmp(ct->name, "drive-bay-fan")) + drives_fan = ct; + else if (!strcmp(ct->name, "cpufreq-clamp")) + cpufreq_clamp = ct; + + all_controls = + cpu_front_fans[0] && + cpu_rear_fans[0] && + backside_fan && + slots_fan && + drives_fan; + if (nr_chips > 1) + all_controls &= + cpu_front_fans[1] && + cpu_rear_fans[1]; + have_all_controls = all_controls; + + if ((cpu_pumps[0] || cpu_pumps[1]) && !had_pump) { + pr_info("wf_pm72: Liquid cooling pump(s) detected," + " using new algorithm !\n"); + cpu_pid_combined = true; + } +} + + +static void pm72_new_sensor(struct wf_sensor *sr) +{ + bool all_sensors; + + if (!strcmp(sr->name, "cpu-diode-temp-0")) + sens_cpu_temp[0] = sr; + else if (!strcmp(sr->name, "cpu-diode-temp-1")) + sens_cpu_temp[1] = sr; + else if (!strcmp(sr->name, "cpu-voltage-0")) + sens_cpu_volts[0] = sr; + else if (!strcmp(sr->name, "cpu-voltage-1")) + sens_cpu_volts[1] = sr; + else if (!strcmp(sr->name, "cpu-current-0")) + sens_cpu_amps[0] = sr; + else if (!strcmp(sr->name, "cpu-current-1")) + sens_cpu_amps[1] = sr; + else if (!strcmp(sr->name, "backside-temp")) + backside_temp = sr; + else if (!strcmp(sr->name, "hd-temp")) + drives_temp = sr; + + all_sensors = + sens_cpu_temp[0] && + sens_cpu_volts[0] && + sens_cpu_amps[0] && + backside_temp && + drives_temp; + if (nr_chips > 1) + all_sensors &= + sens_cpu_temp[1] && + sens_cpu_volts[1] && + sens_cpu_amps[1]; + + have_all_sensors = all_sensors; +} + +static int pm72_wf_notify(struct notifier_block *self, + unsigned long event, void *data) +{ + switch (event) { + case WF_EVENT_NEW_SENSOR: + pm72_new_sensor(data); + break; + case WF_EVENT_NEW_CONTROL: + pm72_new_control(data); + break; + case WF_EVENT_TICK: + if (have_all_controls && have_all_sensors) + pm72_tick(); + } + return 0; +} + +static struct notifier_block pm72_events = { + .notifier_call = pm72_wf_notify, +}; + +static int wf_pm72_probe(struct platform_device *dev) +{ + wf_register_client(&pm72_events); + return 0; +} + +static int wf_pm72_remove(struct platform_device *dev) +{ + wf_unregister_client(&pm72_events); + + /* should release all sensors and controls */ + return 0; +} + +static struct platform_driver wf_pm72_driver = { + .probe = wf_pm72_probe, + .remove = wf_pm72_remove, + .driver = { + .name = "windfarm", + .owner = THIS_MODULE, + }, +}; + +static int __init wf_pm72_init(void) +{ + struct device_node *cpu; + int i; + + if (!of_machine_is_compatible("PowerMac7,2") && + !of_machine_is_compatible("PowerMac7,3")) + return -ENODEV; + + /* Count the number of CPU cores */ + nr_chips = 0; + for_each_node_by_type(cpu, "cpu") + ++nr_chips; + if (nr_chips > NR_CHIPS) + nr_chips = NR_CHIPS; + + pr_info("windfarm: Initializing for desktop G5 with %d chips\n", + nr_chips); + + /* Get MPU data for each CPU */ + for (i = 0; i < nr_chips; i++) { + cpu_mpu_data[i] = wf_get_mpu(i); + if (!cpu_mpu_data[i]) { + pr_err("wf_pm72: Failed to find MPU data for CPU %d\n", i); + return -ENXIO; + } + } + +#ifdef MODULE + request_module("windfarm_fcu_controls"); + request_module("windfarm_lm75_sensor"); + request_module("windfarm_ad7417_sensor"); + request_module("windfarm_max6690_sensor"); + request_module("windfarm_cpufreq_clamp"); +#endif /* MODULE */ + + platform_driver_register(&wf_pm72_driver); + return 0; +} + +static void __exit wf_pm72_exit(void) +{ + platform_driver_unregister(&wf_pm72_driver); +} + +module_init(wf_pm72_init); +module_exit(wf_pm72_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("Thermal control for AGP PowerMac G5s"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:windfarm"); diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index 749d174b0dc..2a5e1b15b1d 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c @@ -107,7 +107,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/smu.h> @@ -150,6 +149,7 @@ static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started; static unsigned int wf_smu_failure_state; static int wf_smu_readjust, wf_smu_skipping; +static bool wf_smu_overtemp; /* * ****** System Fans Control Loop ****** @@ -303,13 +303,13 @@ static void wf_smu_create_sys_fans(void) pid_param.interval = WF_SMU_SYS_FANS_INTERVAL; pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE; pid_param.itarget = param->itarget; - pid_param.min = fan_system->ops->get_min(fan_system); - pid_param.max = fan_system->ops->get_max(fan_system); + pid_param.min = wf_control_get_min(fan_system); + pid_param.max = wf_control_get_max(fan_system); if (fan_hd) { pid_param.min = - max(pid_param.min,fan_hd->ops->get_min(fan_hd)); + max(pid_param.min, wf_control_get_min(fan_hd)); pid_param.max = - min(pid_param.max,fan_hd->ops->get_max(fan_hd)); + min(pid_param.max, wf_control_get_max(fan_hd)); } wf_pid_init(&wf_smu_sys_fans->pid, &pid_param); @@ -338,7 +338,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) } st->ticks = WF_SMU_SYS_FANS_INTERVAL; - rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); + rc = wf_sensor_get(sensor_hd_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", rc); @@ -374,7 +374,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) st->hd_setpoint = new_setpoint; readjust: if (fan_system && wf_smu_failure_state == 0) { - rc = fan_system->ops->set_value(fan_system, st->sys_setpoint); + rc = wf_control_set(fan_system, st->sys_setpoint); if (rc) { printk(KERN_WARNING "windfarm: Sys fan error %d\n", rc); @@ -382,7 +382,7 @@ static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st) } } if (fan_hd && wf_smu_failure_state == 0) { - rc = fan_hd->ops->set_value(fan_hd, st->hd_setpoint); + rc = wf_control_set(fan_hd, st->hd_setpoint); if (rc) { printk(KERN_WARNING "windfarm: HD fan error %d\n", rc); @@ -448,8 +448,8 @@ static void wf_smu_create_cpu_fans(void) pid_param.ttarget = tmax - tdelta; pid_param.pmaxadj = maxpow - powadj; - pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); - pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); + pid_param.min = wf_control_get_min(fan_cpu_main); + pid_param.max = wf_control_get_max(fan_cpu_main); wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); @@ -482,7 +482,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } st->ticks = WF_SMU_CPU_FANS_INTERVAL; - rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); + rc = wf_sensor_get(sensor_cpu_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", rc); @@ -490,7 +490,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) return; } - rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); + rc = wf_sensor_get(sensor_cpu_power, &power); if (rc) { printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", rc); @@ -526,8 +526,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) st->cpu_setpoint = new_setpoint; readjust: if (fan_cpu_main && wf_smu_failure_state == 0) { - rc = fan_cpu_main->ops->set_value(fan_cpu_main, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU main fan" " error %d\n", rc); @@ -595,6 +594,7 @@ static void wf_smu_tick(void) if (new_failure & FAILURE_OVERTEMP) { wf_set_overtemp(); wf_smu_skipping = 2; + wf_smu_overtemp = true; } /* We only clear the overtemp condition if overtemp is cleared @@ -603,8 +603,10 @@ static void wf_smu_tick(void) * the control loop levels, but we don't want to keep it clear * here in this case */ - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP) + if (!wf_smu_failure_state && wf_smu_overtemp) { wf_clear_overtemp(); + wf_smu_overtemp = false; + } } static void wf_smu_new_control(struct wf_control *ct) @@ -722,7 +724,7 @@ static int wf_smu_probe(struct platform_device *ddev) return 0; } -static int __devexit wf_smu_remove(struct platform_device *ddev) +static int wf_smu_remove(struct platform_device *ddev) { wf_unregister_client(&wf_smu_events); @@ -765,7 +767,7 @@ static int __devexit wf_smu_remove(struct platform_device *ddev) static struct platform_driver wf_smu_driver = { .probe = wf_smu_probe, - .remove = __devexit_p(wf_smu_remove), + .remove = wf_smu_remove, .driver = { .name = "windfarm", .owner = THIS_MODULE, diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index 34427323512..a8ac66cd3b1 100644 --- a/drivers/macintosh/windfarm_pm91.c +++ b/drivers/macintosh/windfarm_pm91.c @@ -41,7 +41,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/smu.h> @@ -77,6 +76,7 @@ static struct wf_control *cpufreq_clamp; /* Set to kick the control loop into life */ static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok, wf_smu_started; +static bool wf_smu_overtemp; /* Failure handling.. could be nicer */ #define FAILURE_FAN 0x01 @@ -193,8 +193,8 @@ static void wf_smu_create_cpu_fans(void) pid_param.ttarget = tmax - tdelta; pid_param.pmaxadj = maxpow - powadj; - pid_param.min = fan_cpu_main->ops->get_min(fan_cpu_main); - pid_param.max = fan_cpu_main->ops->get_max(fan_cpu_main); + pid_param.min = wf_control_get_min(fan_cpu_main); + pid_param.max = wf_control_get_max(fan_cpu_main); wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param); @@ -227,7 +227,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } st->ticks = WF_SMU_CPU_FANS_INTERVAL; - rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp); + rc = wf_sensor_get(sensor_cpu_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n", rc); @@ -235,7 +235,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) return; } - rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power); + rc = wf_sensor_get(sensor_cpu_power, &power); if (rc) { printk(KERN_WARNING "windfarm: CPU power sensor error %d\n", rc); @@ -262,8 +262,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) st->cpu_setpoint = new_setpoint; readjust: if (fan_cpu_main && wf_smu_failure_state == 0) { - rc = fan_cpu_main->ops->set_value(fan_cpu_main, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_main, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU main fan" " error %d\n", rc); @@ -271,8 +270,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } } if (fan_cpu_second && wf_smu_failure_state == 0) { - rc = fan_cpu_second->ops->set_value(fan_cpu_second, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_second, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU second fan" " error %d\n", rc); @@ -280,8 +278,7 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) } } if (fan_cpu_third && wf_smu_failure_state == 0) { - rc = fan_cpu_main->ops->set_value(fan_cpu_third, - st->cpu_setpoint); + rc = wf_control_set(fan_cpu_third, st->cpu_setpoint); if (rc) { printk(KERN_WARNING "windfarm: CPU third fan" " error %d\n", rc); @@ -313,8 +310,8 @@ static void wf_smu_create_drive_fans(void) /* Fill PID params */ param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN); - param.min = fan_hd->ops->get_min(fan_hd); - param.max = fan_hd->ops->get_max(fan_hd); + param.min = wf_control_get_min(fan_hd); + param.max = wf_control_get_max(fan_hd); wf_pid_init(&wf_smu_drive_fans->pid, ¶m); DBG("wf: Drive Fan control initialized.\n"); @@ -339,7 +336,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st) } st->ticks = st->pid.param.interval; - rc = sensor_hd_temp->ops->get_value(sensor_hd_temp, &temp); + rc = wf_sensor_get(sensor_hd_temp, &temp); if (rc) { printk(KERN_WARNING "windfarm: HD temp sensor error %d\n", rc); @@ -362,7 +359,7 @@ static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st) st->setpoint = new_setpoint; readjust: if (fan_hd && wf_smu_failure_state == 0) { - rc = fan_hd->ops->set_value(fan_hd, st->setpoint); + rc = wf_control_set(fan_hd, st->setpoint); if (rc) { printk(KERN_WARNING "windfarm: HD fan error %d\n", rc); @@ -394,8 +391,8 @@ static void wf_smu_create_slots_fans(void) /* Fill PID params */ param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN); - param.min = fan_slots->ops->get_min(fan_slots); - param.max = fan_slots->ops->get_max(fan_slots); + param.min = wf_control_get_min(fan_slots); + param.max = wf_control_get_max(fan_slots); wf_pid_init(&wf_smu_slots_fans->pid, ¶m); DBG("wf: Slots Fan control initialized.\n"); @@ -420,7 +417,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) } st->ticks = st->pid.param.interval; - rc = sensor_slots_power->ops->get_value(sensor_slots_power, &power); + rc = wf_sensor_get(sensor_slots_power, &power); if (rc) { printk(KERN_WARNING "windfarm: Slots power sensor error %d\n", rc); @@ -445,7 +442,7 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) st->setpoint = new_setpoint; readjust: if (fan_slots && wf_smu_failure_state == 0) { - rc = fan_slots->ops->set_value(fan_slots, st->setpoint); + rc = wf_control_set(fan_slots, st->setpoint); if (rc) { printk(KERN_WARNING "windfarm: Slots fan error %d\n", rc); @@ -521,6 +518,7 @@ static void wf_smu_tick(void) if (new_failure & FAILURE_OVERTEMP) { wf_set_overtemp(); wf_smu_skipping = 2; + wf_smu_overtemp = true; } /* We only clear the overtemp condition if overtemp is cleared @@ -529,8 +527,10 @@ static void wf_smu_tick(void) * the control loop levels, but we don't want to keep it clear * here in this case */ - if (new_failure == 0 && last_failure & FAILURE_OVERTEMP) + if (!wf_smu_failure_state && wf_smu_overtemp) { wf_clear_overtemp(); + wf_smu_overtemp = false; + } } @@ -646,7 +646,7 @@ static int wf_smu_probe(struct platform_device *ddev) return 0; } -static int __devexit wf_smu_remove(struct platform_device *ddev) +static int wf_smu_remove(struct platform_device *ddev) { wf_unregister_client(&wf_smu_events); @@ -696,7 +696,7 @@ static int __devexit wf_smu_remove(struct platform_device *ddev) static struct platform_driver wf_smu_driver = { .probe = wf_smu_probe, - .remove = __devexit_p(wf_smu_remove), + .remove = wf_smu_remove, .driver = { .name = "windfarm", .owner = THIS_MODULE, diff --git a/drivers/macintosh/windfarm_rm31.c b/drivers/macintosh/windfarm_rm31.c new file mode 100644 index 00000000000..82fc86a90c1 --- /dev/null +++ b/drivers/macintosh/windfarm_rm31.c @@ -0,0 +1,740 @@ +/* + * Windfarm PowerMac thermal control. + * Control loops for RackMack3,1 (Xserve G5) + * + * Copyright (C) 2012 Benjamin Herrenschmidt, IBM Corp. + * + * Use and redistribute under the terms of the GNU GPL v2. + */ +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <asm/prom.h> +#include <asm/smu.h> + +#include "windfarm.h" +#include "windfarm_pid.h" +#include "windfarm_mpu.h" + +#define VERSION "1.0" + +#undef DEBUG +#undef LOTSA_DEBUG + +#ifdef DEBUG +#define DBG(args...) printk(args) +#else +#define DBG(args...) do { } while(0) +#endif + +#ifdef LOTSA_DEBUG +#define DBG_LOTS(args...) printk(args) +#else +#define DBG_LOTS(args...) do { } while(0) +#endif + +/* define this to force CPU overtemp to 60 degree, useful for testing + * the overtemp code + */ +#undef HACKED_OVERTEMP + +/* We currently only handle 2 chips */ +#define NR_CHIPS 2 +#define NR_CPU_FANS 3 * NR_CHIPS + +/* Controls and sensors */ +static struct wf_sensor *sens_cpu_temp[NR_CHIPS]; +static struct wf_sensor *sens_cpu_volts[NR_CHIPS]; +static struct wf_sensor *sens_cpu_amps[NR_CHIPS]; +static struct wf_sensor *backside_temp; +static struct wf_sensor *slots_temp; +static struct wf_sensor *dimms_temp; + +static struct wf_control *cpu_fans[NR_CHIPS][3]; +static struct wf_control *backside_fan; +static struct wf_control *slots_fan; +static struct wf_control *cpufreq_clamp; + +/* We keep a temperature history for average calculation of 180s */ +#define CPU_TEMP_HIST_SIZE 180 + +/* PID loop state */ +static const struct mpu_data *cpu_mpu_data[NR_CHIPS]; +static struct wf_cpu_pid_state cpu_pid[NR_CHIPS]; +static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; +static int cpu_thist_pt; +static s64 cpu_thist_total; +static s32 cpu_all_tmax = 100 << 16; +static struct wf_pid_state backside_pid; +static int backside_tick; +static struct wf_pid_state slots_pid; +static int slots_tick; +static int slots_speed; +static struct wf_pid_state dimms_pid; +static int dimms_output_clamp; + +static int nr_chips; +static bool have_all_controls; +static bool have_all_sensors; +static bool started; + +static int failure_state; +#define FAILURE_SENSOR 1 +#define FAILURE_FAN 2 +#define FAILURE_PERM 4 +#define FAILURE_LOW_OVERTEMP 8 +#define FAILURE_HIGH_OVERTEMP 16 + +/* Overtemp values */ +#define LOW_OVER_AVERAGE 0 +#define LOW_OVER_IMMEDIATE (10 << 16) +#define LOW_OVER_CLEAR ((-10) << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) +#define HIGH_OVER_AVERAGE (10 << 16) +#define HIGH_OVER_IMMEDIATE (14 << 16) + + +static void cpu_max_all_fans(void) +{ + int i; + + /* We max all CPU fans in case of a sensor error. We also do the + * cpufreq clamping now, even if it's supposedly done later by the + * generic code anyway, we do it earlier here to react faster + */ + if (cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + for (i = 0; i < nr_chips; i++) { + if (cpu_fans[i][0]) + wf_control_set_max(cpu_fans[i][0]); + if (cpu_fans[i][1]) + wf_control_set_max(cpu_fans[i][1]); + if (cpu_fans[i][2]) + wf_control_set_max(cpu_fans[i][2]); + } +} + +static int cpu_check_overtemp(s32 temp) +{ + int new_state = 0; + s32 t_avg, t_old; + static bool first = true; + + /* First check for immediate overtemps */ + if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" + " temperature !\n"); + } + if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " immediate CPU temperature !\n"); + } + + /* + * The first time around, initialize the array with the first + * temperature reading + */ + if (first) { + int i; + + cpu_thist_total = 0; + for (i = 0; i < CPU_TEMP_HIST_SIZE; i++) { + cpu_thist[i] = temp; + cpu_thist_total += temp; + } + first = false; + } + + /* + * We calculate a history of max temperatures and use that for the + * overtemp management + */ + t_old = cpu_thist[cpu_thist_pt]; + cpu_thist[cpu_thist_pt] = temp; + cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; + cpu_thist_total -= t_old; + cpu_thist_total += temp; + t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; + + DBG_LOTS(" t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", + FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); + + /* Now check for average overtemps */ + if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { + new_state |= FAILURE_LOW_OVERTEMP; + if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Overtemp due to average CPU" + " temperature !\n"); + } + if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { + new_state |= FAILURE_HIGH_OVERTEMP; + if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) + printk(KERN_ERR "windfarm: Critical overtemp due to" + " average CPU temperature !\n"); + } + + /* Now handle overtemp conditions. We don't currently use the windfarm + * overtemp handling core as it's not fully suited to the needs of those + * new machine. This will be fixed later. + */ + if (new_state) { + /* High overtemp -> immediate shutdown */ + if (new_state & FAILURE_HIGH_OVERTEMP) + machine_power_off(); + if ((failure_state & new_state) != new_state) + cpu_max_all_fans(); + failure_state |= new_state; + } else if ((failure_state & FAILURE_LOW_OVERTEMP) && + (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { + printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); + failure_state &= ~FAILURE_LOW_OVERTEMP; + } + + return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); +} + +static int read_one_cpu_vals(int cpu, s32 *temp, s32 *power) +{ + s32 dtemp, volts, amps; + int rc; + + /* Get diode temperature */ + rc = wf_sensor_get(sens_cpu_temp[cpu], &dtemp); + if (rc) { + DBG(" CPU%d: temp reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: temp = %d.%03d\n", cpu, FIX32TOPRINT((dtemp))); + *temp = dtemp; + + /* Get voltage */ + rc = wf_sensor_get(sens_cpu_volts[cpu], &volts); + if (rc) { + DBG(" CPU%d, volts reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: volts = %d.%03d\n", cpu, FIX32TOPRINT((volts))); + + /* Get current */ + rc = wf_sensor_get(sens_cpu_amps[cpu], &s); + if (rc) { + DBG(" CPU%d, current reading error !\n", cpu); + return -EIO; + } + DBG_LOTS(" CPU%d: amps = %d.%03d\n", cpu, FIX32TOPRINT((amps))); + + /* Calculate power */ + + /* Scale voltage and current raw sensor values according to fixed scales + * obtained in Darwin and calculate power from I and V + */ + *power = (((u64)volts) * ((u64)amps)) >> 16; + + DBG_LOTS(" CPU%d: power = %d.%03d\n", cpu, FIX32TOPRINT((*power))); + + return 0; + +} + +static void cpu_fans_tick(void) +{ + int err, cpu, i; + s32 speed, temp, power, t_max = 0; + + DBG_LOTS("* cpu fans_tick_split()\n"); + + for (cpu = 0; cpu < nr_chips; ++cpu) { + struct wf_cpu_pid_state *sp = &cpu_pid[cpu]; + + /* Read current speed */ + wf_control_get(cpu_fans[cpu][0], &sp->target); + + err = read_one_cpu_vals(cpu, &temp, &power); + if (err) { + failure_state |= FAILURE_SENSOR; + cpu_max_all_fans(); + return; + } + + /* Keep track of highest temp */ + t_max = max(t_max, temp); + + /* Handle possible overtemps */ + if (cpu_check_overtemp(t_max)) + return; + + /* Run PID */ + wf_cpu_pid_run(sp, power, temp); + + DBG_LOTS(" CPU%d: target = %d RPM\n", cpu, sp->target); + + /* Apply DIMMs clamp */ + speed = max(sp->target, dimms_output_clamp); + + /* Apply result to all cpu fans */ + for (i = 0; i < 3; i++) { + err = wf_control_set(cpu_fans[cpu][i], speed); + if (err) { + pr_warning("wf_rm31: Fan %s reports error %d\n", + cpu_fans[cpu][i]->name, err); + failure_state |= FAILURE_FAN; + } + } + } +} + +/* Implementation... */ +static int cpu_setup_pid(int cpu) +{ + struct wf_cpu_pid_param pid; + const struct mpu_data *mpu = cpu_mpu_data[cpu]; + s32 tmax, ttarget, ptarget; + int fmin, fmax, hsize; + + /* Get PID params from the appropriate MPU EEPROM */ + tmax = mpu->tmax << 16; + ttarget = mpu->ttarget << 16; + ptarget = ((s32)(mpu->pmaxh - mpu->padjmax)) << 16; + + DBG("wf_72: CPU%d ttarget = %d.%03d, tmax = %d.%03d\n", + cpu, FIX32TOPRINT(ttarget), FIX32TOPRINT(tmax)); + + /* We keep a global tmax for overtemp calculations */ + if (tmax < cpu_all_tmax) + cpu_all_tmax = tmax; + + /* Set PID min/max by using the rear fan min/max */ + fmin = wf_control_get_min(cpu_fans[cpu][0]); + fmax = wf_control_get_max(cpu_fans[cpu][0]); + DBG("wf_72: CPU%d max RPM range = [%d..%d]\n", cpu, fmin, fmax); + + /* History size */ + hsize = min_t(int, mpu->tguardband, WF_PID_MAX_HISTORY); + DBG("wf_72: CPU%d history size = %d\n", cpu, hsize); + + /* Initialize PID loop */ + pid.interval = 1; /* seconds */ + pid.history_len = hsize; + pid.gd = mpu->pid_gd; + pid.gp = mpu->pid_gp; + pid.gr = mpu->pid_gr; + pid.tmax = tmax; + pid.ttarget = ttarget; + pid.pmaxadj = ptarget; + pid.min = fmin; + pid.max = fmax; + + wf_cpu_pid_init(&cpu_pid[cpu], &pid); + cpu_pid[cpu].target = 4000; + + return 0; +} + +/* Backside/U3 fan */ +static struct wf_pid_param backside_param = { + .interval = 1, + .history_len = 2, + .gd = 0x00500000, + .gp = 0x0004cccc, + .gr = 0, + .itarget = 70 << 16, + .additive = 0, + .min = 20, + .max = 100, +}; + +/* DIMMs temperature (clamp the backside fan) */ +static struct wf_pid_param dimms_param = { + .interval = 1, + .history_len = 20, + .gd = 0, + .gp = 0, + .gr = 0x06553600, + .itarget = 50 << 16, + .additive = 0, + .min = 4000, + .max = 14000, +}; + +static void backside_fan_tick(void) +{ + s32 temp, dtemp; + int speed, dspeed, fan_min; + int err; + + if (!backside_fan || !backside_temp || !dimms_temp || !backside_tick) + return; + if (--backside_tick > 0) + return; + backside_tick = backside_pid.param.interval; + + DBG_LOTS("* backside fans tick\n"); + + /* Update fan speed from actual fans */ + err = wf_control_get(backside_fan, &speed); + if (!err) + backside_pid.target = speed; + + err = wf_sensor_get(backside_temp, &temp); + if (err) { + printk(KERN_WARNING "windfarm: U3 temp sensor error %d\n", + err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(backside_fan); + return; + } + speed = wf_pid_run(&backside_pid, temp); + + DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + err = wf_sensor_get(dimms_temp, &dtemp); + if (err) { + printk(KERN_WARNING "windfarm: DIMMs temp sensor error %d\n", + err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(backside_fan); + return; + } + dspeed = wf_pid_run(&dimms_pid, dtemp); + dimms_output_clamp = dspeed; + + fan_min = (dspeed * 100) / 14000; + fan_min = max(fan_min, backside_param.min); + speed = max(speed, fan_min); + + err = wf_control_set(backside_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: backside fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void backside_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(backside_fan); + s32 fmax = wf_control_get_max(backside_fan); + struct wf_pid_param param; + + param = backside_param; + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&backside_pid, ¶m); + + param = dimms_param; + wf_pid_init(&dimms_pid, ¶m); + + backside_tick = 1; + + pr_info("wf_rm31: Backside control loop started.\n"); +} + +/* Slots fan */ +static const struct wf_pid_param slots_param = { + .interval = 1, + .history_len = 20, + .gd = 0, + .gp = 0, + .gr = 0x00100000, + .itarget = 3200000, + .additive = 0, + .min = 20, + .max = 100, +}; + +static void slots_fan_tick(void) +{ + s32 temp; + int speed; + int err; + + if (!slots_fan || !slots_temp || !slots_tick) + return; + if (--slots_tick > 0) + return; + slots_tick = slots_pid.param.interval; + + DBG_LOTS("* slots fans tick\n"); + + err = wf_sensor_get(slots_temp, &temp); + if (err) { + pr_warning("wf_rm31: slots temp sensor error %d\n", err); + failure_state |= FAILURE_SENSOR; + wf_control_set_max(slots_fan); + return; + } + speed = wf_pid_run(&slots_pid, temp); + + DBG_LOTS("slots PID temp=%d.%.3d speed=%d\n", + FIX32TOPRINT(temp), speed); + + slots_speed = speed; + err = wf_control_set(slots_fan, speed); + if (err) { + printk(KERN_WARNING "windfarm: slots bay fan error %d\n", err); + failure_state |= FAILURE_FAN; + } +} + +static void slots_setup_pid(void) +{ + /* first time initialize things */ + s32 fmin = wf_control_get_min(slots_fan); + s32 fmax = wf_control_get_max(slots_fan); + struct wf_pid_param param = slots_param; + + param.min = max(param.min, fmin); + param.max = min(param.max, fmax); + wf_pid_init(&slots_pid, ¶m); + slots_tick = 1; + + pr_info("wf_rm31: Slots control loop started.\n"); +} + +static void set_fail_state(void) +{ + cpu_max_all_fans(); + + if (backside_fan) + wf_control_set_max(backside_fan); + if (slots_fan) + wf_control_set_max(slots_fan); +} + +static void rm31_tick(void) +{ + int i, last_failure; + + if (!started) { + started = 1; + printk(KERN_INFO "windfarm: CPUs control loops started.\n"); + for (i = 0; i < nr_chips; ++i) { + if (cpu_setup_pid(i) < 0) { + failure_state = FAILURE_PERM; + set_fail_state(); + break; + } + } + DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); + + backside_setup_pid(); + slots_setup_pid(); + +#ifdef HACKED_OVERTEMP + cpu_all_tmax = 60 << 16; +#endif + } + + /* Permanent failure, bail out */ + if (failure_state & FAILURE_PERM) + return; + + /* + * Clear all failure bits except low overtemp which will be eventually + * cleared by the control loop itself + */ + last_failure = failure_state; + failure_state &= FAILURE_LOW_OVERTEMP; + backside_fan_tick(); + slots_fan_tick(); + + /* We do CPUs last because they can be clamped high by + * DIMM temperature + */ + cpu_fans_tick(); + + DBG_LOTS(" last_failure: 0x%x, failure_state: %x\n", + last_failure, failure_state); + + /* Check for failures. Any failure causes cpufreq clamping */ + if (failure_state && last_failure == 0 && cpufreq_clamp) + wf_control_set_max(cpufreq_clamp); + if (failure_state == 0 && last_failure && cpufreq_clamp) + wf_control_set_min(cpufreq_clamp); + + /* That's it for now, we might want to deal with other failures + * differently in the future though + */ +} + +static void rm31_new_control(struct wf_control *ct) +{ + bool all_controls; + + if (!strcmp(ct->name, "cpu-fan-a-0")) + cpu_fans[0][0] = ct; + else if (!strcmp(ct->name, "cpu-fan-b-0")) + cpu_fans[0][1] = ct; + else if (!strcmp(ct->name, "cpu-fan-c-0")) + cpu_fans[0][2] = ct; + else if (!strcmp(ct->name, "cpu-fan-a-1")) + cpu_fans[1][0] = ct; + else if (!strcmp(ct->name, "cpu-fan-b-1")) + cpu_fans[1][1] = ct; + else if (!strcmp(ct->name, "cpu-fan-c-1")) + cpu_fans[1][2] = ct; + else if (!strcmp(ct->name, "backside-fan")) + backside_fan = ct; + else if (!strcmp(ct->name, "slots-fan")) + slots_fan = ct; + else if (!strcmp(ct->name, "cpufreq-clamp")) + cpufreq_clamp = ct; + + all_controls = + cpu_fans[0][0] && + cpu_fans[0][1] && + cpu_fans[0][2] && + backside_fan && + slots_fan; + if (nr_chips > 1) + all_controls &= + cpu_fans[1][0] && + cpu_fans[1][1] && + cpu_fans[1][2]; + have_all_controls = all_controls; +} + + +static void rm31_new_sensor(struct wf_sensor *sr) +{ + bool all_sensors; + + if (!strcmp(sr->name, "cpu-diode-temp-0")) + sens_cpu_temp[0] = sr; + else if (!strcmp(sr->name, "cpu-diode-temp-1")) + sens_cpu_temp[1] = sr; + else if (!strcmp(sr->name, "cpu-voltage-0")) + sens_cpu_volts[0] = sr; + else if (!strcmp(sr->name, "cpu-voltage-1")) + sens_cpu_volts[1] = sr; + else if (!strcmp(sr->name, "cpu-current-0")) + sens_cpu_amps[0] = sr; + else if (!strcmp(sr->name, "cpu-current-1")) + sens_cpu_amps[1] = sr; + else if (!strcmp(sr->name, "backside-temp")) + backside_temp = sr; + else if (!strcmp(sr->name, "slots-temp")) + slots_temp = sr; + else if (!strcmp(sr->name, "dimms-temp")) + dimms_temp = sr; + + all_sensors = + sens_cpu_temp[0] && + sens_cpu_volts[0] && + sens_cpu_amps[0] && + backside_temp && + slots_temp && + dimms_temp; + if (nr_chips > 1) + all_sensors &= + sens_cpu_temp[1] && + sens_cpu_volts[1] && + sens_cpu_amps[1]; + + have_all_sensors = all_sensors; +} + +static int rm31_wf_notify(struct notifier_block *self, + unsigned long event, void *data) +{ + switch (event) { + case WF_EVENT_NEW_SENSOR: + rm31_new_sensor(data); + break; + case WF_EVENT_NEW_CONTROL: + rm31_new_control(data); + break; + case WF_EVENT_TICK: + if (have_all_controls && have_all_sensors) + rm31_tick(); + } + return 0; +} + +static struct notifier_block rm31_events = { + .notifier_call = rm31_wf_notify, +}; + +static int wf_rm31_probe(struct platform_device *dev) +{ + wf_register_client(&rm31_events); + return 0; +} + +static int wf_rm31_remove(struct platform_device *dev) +{ + wf_unregister_client(&rm31_events); + + /* should release all sensors and controls */ + return 0; +} + +static struct platform_driver wf_rm31_driver = { + .probe = wf_rm31_probe, + .remove = wf_rm31_remove, + .driver = { + .name = "windfarm", + .owner = THIS_MODULE, + }, +}; + +static int __init wf_rm31_init(void) +{ + struct device_node *cpu; + int i; + + if (!of_machine_is_compatible("RackMac3,1")) + return -ENODEV; + + /* Count the number of CPU cores */ + nr_chips = 0; + for_each_node_by_type(cpu, "cpu") + ++nr_chips; + if (nr_chips > NR_CHIPS) + nr_chips = NR_CHIPS; + + pr_info("windfarm: Initializing for desktop G5 with %d chips\n", + nr_chips); + + /* Get MPU data for each CPU */ + for (i = 0; i < nr_chips; i++) { + cpu_mpu_data[i] = wf_get_mpu(i); + if (!cpu_mpu_data[i]) { + pr_err("wf_rm31: Failed to find MPU data for CPU %d\n", i); + return -ENXIO; + } + } + +#ifdef MODULE + request_module("windfarm_fcu_controls"); + request_module("windfarm_lm75_sensor"); + request_module("windfarm_lm87_sensor"); + request_module("windfarm_ad7417_sensor"); + request_module("windfarm_max6690_sensor"); + request_module("windfarm_cpufreq_clamp"); +#endif /* MODULE */ + + platform_driver_register(&wf_rm31_driver); + return 0; +} + +static void __exit wf_rm31_exit(void) +{ + platform_driver_unregister(&wf_rm31_driver); +} + +module_init(wf_rm31_init); +module_exit(wf_rm31_exit); + +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); +MODULE_DESCRIPTION("Thermal control for Xserve G5"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:windfarm"); diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index 43137b421f9..c155a54e863 100644 --- a/drivers/macintosh/windfarm_smu_controls.c +++ b/drivers/macintosh/windfarm_smu_controls.c @@ -18,7 +18,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/smu.h> @@ -173,7 +172,6 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, fct->fan_type = pwm_fan; fct->ctrl.type = pwm_fan ? WF_CONTROL_PWM_FAN : WF_CONTROL_RPM_FAN; - sysfs_attr_init(&fct->ctrl.attr.attr); /* We use the name & location here the same way we do for SMU sensors, * 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 65a8ff3e1f8..ad6223e8834 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -20,7 +20,7 @@ #include "windfarm.h" -#define VERSION "0.2" +#define VERSION "1.0" #define DEBUG @@ -34,11 +34,12 @@ #define MAX_AGE msecs_to_jiffies(800) struct wf_sat { + struct kref ref; int nr; - atomic_t refcnt; struct mutex mutex; unsigned long last_read; /* jiffies when cache last updated */ u8 cache[16]; + struct list_head sensors; struct i2c_client *i2c; struct device_node *node; }; @@ -46,11 +47,12 @@ struct wf_sat { static struct wf_sat *sats[2]; struct wf_sat_sensor { - int index; - int index2; /* used for power sensors */ - int shift; - struct wf_sat *sat; - struct wf_sensor sens; + struct list_head link; + int index; + int index2; /* used for power sensors */ + int shift; + struct wf_sat *sat; + struct wf_sensor sens; }; #define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) @@ -142,7 +144,7 @@ static int wf_sat_read_cache(struct wf_sat *sat) return 0; } -static int wf_sat_get(struct wf_sensor *sr, s32 *value) +static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value) { struct wf_sat_sensor *sens = wf_to_sat(sr); struct wf_sat *sat = sens->sat; @@ -175,62 +177,34 @@ static int wf_sat_get(struct wf_sensor *sr, s32 *value) return err; } -static void wf_sat_release(struct wf_sensor *sr) +static void wf_sat_release(struct kref *ref) +{ + struct wf_sat *sat = container_of(ref, struct wf_sat, ref); + + if (sat->nr >= 0) + sats[sat->nr] = NULL; + kfree(sat); +} + +static void wf_sat_sensor_release(struct wf_sensor *sr) { struct wf_sat_sensor *sens = wf_to_sat(sr); struct wf_sat *sat = sens->sat; - if (atomic_dec_and_test(&sat->refcnt)) { - if (sat->nr >= 0) - sats[sat->nr] = NULL; - kfree(sat); - } kfree(sens); + kref_put(&sat->ref, wf_sat_release); } static struct wf_sensor_ops wf_sat_ops = { - .get_value = wf_sat_get, - .release = wf_sat_release, + .get_value = wf_sat_sensor_get, + .release = wf_sat_sensor_release, .owner = THIS_MODULE, }; -static struct i2c_driver wf_sat_driver; - -static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) -{ - struct i2c_board_info info; - struct i2c_client *client; - const u32 *reg; - u8 addr; - - reg = of_get_property(dev, "reg", NULL); - if (reg == NULL) - return; - addr = *reg; - DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr); - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = (addr >> 1) & 0x7f; - info.platform_data = dev; - strlcpy(info.type, "wf_sat", I2C_NAME_SIZE); - - client = i2c_new_device(adapter, &info); - if (client == NULL) { - printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n"); - return; - } - - /* - * Let i2c-core delete that device on driver removal. - * This is safe because i2c-core holds the core_lock mutex for us. - */ - list_add_tail(&client->detected, &wf_sat_driver.clients); -} - static int wf_sat_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct device_node *dev = client->dev.platform_data; + struct device_node *dev = client->dev.of_node; struct wf_sat *sat; struct wf_sat_sensor *sens; const u32 *reg; @@ -246,9 +220,10 @@ static int wf_sat_probe(struct i2c_client *client, return -ENOMEM; sat->nr = -1; sat->node = of_node_get(dev); - atomic_set(&sat->refcnt, 0); + kref_init(&sat->ref); mutex_init(&sat->mutex); sat->i2c = client; + INIT_LIST_HEAD(&sat->sensors); i2c_set_clientdata(client, sat); vsens[0] = vsens[1] = -1; @@ -310,14 +285,15 @@ static int wf_sat_probe(struct i2c_client *client, sens->index2 = -1; sens->shift = shift; sens->sat = sat; - atomic_inc(&sat->refcnt); sens->sens.ops = &wf_sat_ops; sens->sens.name = (char *) (sens + 1); - snprintf(sens->sens.name, 16, "%s-%d", name, cpu); + snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu); - if (wf_register_sensor(&sens->sens)) { - atomic_dec(&sat->refcnt); + if (wf_register_sensor(&sens->sens)) kfree(sens); + else { + list_add(&sens->link, &sat->sensors); + kref_get(&sat->ref); } } @@ -336,14 +312,15 @@ static int wf_sat_probe(struct i2c_client *client, sens->index2 = isens[core]; sens->shift = 0; sens->sat = sat; - atomic_inc(&sat->refcnt); sens->sens.ops = &wf_sat_ops; sens->sens.name = (char *) (sens + 1); - snprintf(sens->sens.name, 16, "cpu-power-%d", cpu); + snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu); - if (wf_register_sensor(&sens->sens)) { - atomic_dec(&sat->refcnt); + if (wf_register_sensor(&sens->sens)) kfree(sens); + else { + list_add(&sens->link, &sat->sensors); + kref_get(&sat->ref); } } @@ -353,61 +330,40 @@ static int wf_sat_probe(struct i2c_client *client, return 0; } -static int wf_sat_attach(struct i2c_adapter *adapter) -{ - struct device_node *busnode, *dev = NULL; - struct pmac_i2c_bus *bus; - - bus = pmac_i2c_adapter_to_bus(adapter); - if (bus == NULL) - return -ENODEV; - busnode = pmac_i2c_get_bus_node(bus); - - while ((dev = of_get_next_child(busnode, dev)) != NULL) - if (of_device_is_compatible(dev, "smu-sat")) - wf_sat_create(adapter, dev); - return 0; -} - static int wf_sat_remove(struct i2c_client *client) { struct wf_sat *sat = i2c_get_clientdata(client); + struct wf_sat_sensor *sens; - /* XXX TODO */ - + /* release sensors */ + while(!list_empty(&sat->sensors)) { + sens = list_first_entry(&sat->sensors, + struct wf_sat_sensor, link); + list_del(&sens->link); + wf_unregister_sensor(&sens->sens); + } sat->i2c = NULL; + kref_put(&sat->ref, wf_sat_release); + return 0; } static const struct i2c_device_id wf_sat_id[] = { - { "wf_sat", 0 }, + { "MAC,smu-sat", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, wf_sat_id); static struct i2c_driver wf_sat_driver = { .driver = { .name = "wf_smu_sat", }, - .attach_adapter = wf_sat_attach, .probe = wf_sat_probe, .remove = wf_sat_remove, .id_table = wf_sat_id, }; -static int __init sat_sensors_init(void) -{ - return i2c_add_driver(&wf_sat_driver); -} - -#if 0 /* uncomment when module_exit() below is uncommented */ -static void __exit sat_sensors_exit(void) -{ - i2c_del_driver(&wf_sat_driver); -} -#endif - -module_init(sat_sensors_init); -/*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */ +module_i2c_driver(wf_sat_driver); MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); 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 3c193504bb8..1cc4e4953d8 100644 --- a/drivers/macintosh/windfarm_smu_sensors.c +++ b/drivers/macintosh/windfarm_smu_sensors.c @@ -18,7 +18,6 @@ #include <asm/prom.h> #include <asm/machdep.h> #include <asm/io.h> -#include <asm/system.h> #include <asm/sections.h> #include <asm/smu.h> |
