diff options
Diffstat (limited to 'drivers/macintosh')
41 files changed, 3508 insertions, 870 deletions
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index 3d7355ff730..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 @@ -102,6 +102,8 @@ config ADB_PMU_LED  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 @@ -203,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" @@ -220,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 @@ -258,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 2ad62c339cd..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_scheduled_work(); +	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 067f9962f49..80d30e8e338 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -23,6 +23,8 @@ static int mouse_button3_keycode = KEY_RIGHTALT;	/* right option key */  static struct input_dev *mac_hid_emumouse_dev; +static DEFINE_MUTEX(mac_hid_emumouse_mutex); +  static int mac_hid_create_emumouse(void)  {  	static struct lock_class_key mac_hid_emumouse_dev_event_class; @@ -179,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)  { @@ -187,6 +189,10 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write,  	int old_val = *valp;  	int rc; +	rc = mutex_lock_killable(&mac_hid_emumouse_mutex); +	if (rc) +		return rc; +  	rc = proc_dointvec(table, write, buffer, lenp, ppos);  	if (rc == 0 && write && *valp != old_val) { @@ -202,11 +208,13 @@ static int mac_hid_toggle_emumouse(ctl_table *table, int write,  	if (rc)  		*valp = old_val; +	mutex_unlock(&mac_hid_emumouse_mutex); +  	return rc;  }  /* 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, @@ -232,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, @@ -243,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 b6e7ddc09d7..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, @@ -387,11 +389,10 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip,  	/* Set the DMA ops to the ones from the PCI device, this could be  	 * fishy if we didn't know that on PowerMac it's always direct ops  	 * or iommu ops that will work fine +	 * +	 * To get all the fields, copy all archdata  	 */ -	dev->ofdev.dev.archdata.dma_ops = -		chip->lbus.pdev->dev.archdata.dma_ops; -	dev->ofdev.dev.archdata.dma_data = -		chip->lbus.pdev->dev.archdata.dma_data; +	dev->ofdev.dev.archdata = chip->lbus.pdev->dev.archdata;  #endif /* CONFIG_PCI */  #ifdef DEBUG @@ -680,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; @@ -740,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");  } @@ -749,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 53cce3a5da2..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_rearming_delayed_work(&rm->cpu[0].sniffer); -	cancel_rearming_delayed_work(&rm->cpu[1].sniffer); +	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 44549272333..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; @@ -443,7 +442,7 @@ static int fan_read_reg(int reg, unsigned char *buf, int nb)  	tries = 0;  	for (;;) {  		nr = i2c_master_recv(fcu, buf, nb); -		if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100) +		if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100)  			break;  		msleep(10);  		++tries; @@ -464,7 +463,7 @@ static int fan_write_reg(int reg, const unsigned char *ptr, int nb)  	tries = 0;  	for (;;) {  		nw = i2c_master_send(fcu, buf, nb); -		if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100) +		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)  			break;  		msleep(10);  		++tries; @@ -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,9 +2209,12 @@ 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; + +	dev_info(&dev->dev, "PowerMac G5 Thermal control driver %s\n", VERSION);  	/* Lookup the fans in the device tree */  	fcu_lookup_fans(dev->dev.of_node); @@ -2235,8 +2237,9 @@ 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", @@ -2252,8 +2255,6 @@ static struct of_platform_driver fcu_of_platform_driver =   */  static int __init therm_pm72_init(void)  { -	struct device_node *np; -  	rackmac = of_machine_is_compatible("RackMac3,1");  	if (!of_machine_is_compatible("PowerMac7,2") && @@ -2261,34 +2262,12 @@ static int __init therm_pm72_init(void)  	    !rackmac)  	    	return -ENODEV; -	printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION); - -	np = of_find_node_by_type(NULL, "fcu"); -	if (np == NULL) { -		/* Some machines have strangely broken device-tree */ -		np = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/fan@15e"); -		if (np == NULL) { -			    printk(KERN_ERR "Can't find FCU in device-tree !\n"); -			    return -ENODEV; -		} -	} -	of_dev = of_platform_device_create(np, "temperature", NULL); -	if (of_dev == NULL) { -		printk(KERN_ERR "Can't register FCU platform device !\n"); -		return -ENODEV; -	} - -	of_register_platform_driver(&fcu_of_platform_driver); -	 -	return 0; +	return platform_driver_register(&fcu_of_platform_driver);  }  static void __exit therm_pm72_exit(void)  { -	of_unregister_platform_driver(&fcu_of_platform_driver); - -	if (of_dev) -		of_device_unregister(of_dev); +	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 1cec02f6c43..b1d91170ded 100644 --- a/drivers/macintosh/via-pmu-backlight.c +++ b/drivers/macintosh/via-pmu-backlight.c @@ -15,7 +15,7 @@  #define MAX_PMU_LEVEL 0xFF -static struct backlight_ops pmu_backlight_data; +static const struct backlight_ops pmu_backlight_data;  static DEFINE_SPINLOCK(pmu_backlight_lock);  static int sleeping, uses_pmu_bl;  static u8 bl_curve[FB_BACKLIGHT_LEVELS]; @@ -115,7 +115,7 @@ static int pmu_backlight_get_brightness(struct backlight_device *bd)  	return bd->props.brightness;  } -static struct backlight_ops pmu_backlight_data = { +static const struct backlight_ops pmu_backlight_data = {  	.get_brightness	= pmu_backlight_get_brightness,  	.update_status	= pmu_backlight_update_status, @@ -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 cd29c824838..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 = { @@ -2257,7 +2259,7 @@ static int pmu_sleep_valid(suspend_state_t state)  		&& (pmac_call_feature(PMAC_FTR_SLEEP_STATE, NULL, 0, -1) >= 0);  } -static struct platform_suspend_ops pmu_pm_ops = { +static const struct platform_suspend_ops pmu_pm_ops = {  	.enter = powerbook_sleep,  	.valid = pmu_sleep_valid,  }; @@ -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>  | 
