Blame view

drivers/macintosh/therm_windtunnel.c 12 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /* 
   *   Creation Date: <2003/03/14 20:54:13 samuel>
   *   Time-stamp: <2004/03/20 14:20:59 samuel>
   *   
   *	<therm_windtunnel.c>
   *	
   *	The G4 "windtunnel" has a single fan controlled by an
   *	ADM1030 fan controller and a DS1775 thermostat.
   *
   *	The fan controller is equipped with a temperature sensor
   *	which measures the case temperature. The DS1775 sensor
   *	measures the CPU temperature. This driver tunes the
   *	behavior of the fan. It is based upon empirical observations
   *	of the 'AppleFan' driver under Mac OS X.
   *
   *	WARNING: This driver has only been testen on Apple's
   *	1.25 MHz Dual G4 (March 03). It is tuned for a CPU
af901ca18   André Goddard Rosa   tree-wide: fix as...
18
   *	temperature around 57 C.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
21
22
23
24
25
26
27
28
   *
   *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
   *
   *   Loosely based upon 'thermostat.c' written by Benjamin Herrenschmidt
   *   
   *   This program is free software; you can redistribute it and/or
   *   modify it under the terms of the GNU General Public License
   *   as published by the Free Software Foundation
   *   
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
32
33
34
35
  #include <linux/types.h>
  #include <linux/module.h>
  #include <linux/errno.h>
  #include <linux/kernel.h>
  #include <linux/delay.h>
  #include <linux/sched.h>
  #include <linux/i2c.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  #include <linux/init.h>
98f6740ea   Paul Mackerras   [POWERPC] Convert...
37
  #include <linux/kthread.h>
ad9e05aef   Stephen Rothwell   macintosh: Use li...
38
  #include <linux/of_platform.h>
7eebde700   Benjamin Herrenschmidt   [POWERPC] Souped-...
39

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
  #include <asm/prom.h>
  #include <asm/machdep.h>
  #include <asm/io.h>
  #include <asm/system.h>
  #include <asm/sections.h>
5e6557722   Jeff Mahoney   [PATCH] openfirmw...
45
  #include <asm/macio.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46

25985edce   Lucas De Marchi   Fix common misspe...
47
  #define LOG_TEMP		0			/* continuously log temperature */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
  static struct {
  	volatile int		running;
98f6740ea   Paul Mackerras   [POWERPC] Convert...
51
  	struct task_struct	*poll_task;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
  	
1baaeea00   Daniel Walker   macintosh/therm_w...
53
  	struct mutex	 	lock;
2dc115813   Grant Likely   of/device: Replac...
54
  	struct platform_device	*of_dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
  	
  	struct i2c_client	*thermostat;
  	struct i2c_client	*fan;
  
  	int			overheat_temp;		/* 100% fan at this temp */
  	int			overheat_hyst;
  	int			temp;
  	int			casetemp;
  	int			fan_level;		/* active fan_table setting */
  
  	int			downind;
  	int			upind;
  
  	int			r0, r1, r20, r23, r25;	/* saved register */
  } x;
  
  #define T(x,y)			(((x)<<8) | (y)*0x100/10 )
  
  static struct {
  	int			fan_down_setting;
  	int			temp;
  	int			fan_up_setting;
  } fan_table[] = {
  	{ 11, T(0,0),  11 },	/* min fan */
  	{ 11, T(55,0), 11 },
  	{  6, T(55,3), 11 },
  	{  7, T(56,0), 11 },
  	{  8, T(57,0), 8 },
  	{  7, T(58,3), 7 },
  	{  6, T(58,8), 6 },
  	{  5, T(59,2), 5 },
  	{  4, T(59,6), 4 },
  	{  3, T(59,9), 3 },
  	{  2, T(60,1), 2 },
  	{  1, 0xfffff, 1 }	/* on fire */
  };
  
  static void
  print_temp( const char *s, int temp )
  {
  	printk("%s%d.%d C", s ? s : "", temp>>8, (temp & 255)*10/256 );
  }
  
  static ssize_t
e404e274f   Yani Ioannou   [PATCH] Driver Co...
99
  show_cpu_temperature( struct device *dev, struct device_attribute *attr, char *buf )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
  {
  	return sprintf(buf, "%d.%d
  ", x.temp>>8, (x.temp & 255)*10/256 );
  }
  
  static ssize_t
e404e274f   Yani Ioannou   [PATCH] Driver Co...
106
  show_case_temperature( struct device *dev, struct device_attribute *attr, char *buf )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  {
  	return sprintf(buf, "%d.%d
  ", x.casetemp>>8, (x.casetemp & 255)*10/256 );
  }
  
  static DEVICE_ATTR(cpu_temperature, S_IRUGO, show_cpu_temperature, NULL );
  static DEVICE_ATTR(case_temperature, S_IRUGO, show_case_temperature, NULL );
  
  
  
  /************************************************************************/
  /*	controller thread						*/
  /************************************************************************/
  
  static int
  write_reg( struct i2c_client *cl, int reg, int data, int len )
  {
  	u8 tmp[3];
  
  	if( len < 1 || len > 2 || data < 0 )
  		return -EINVAL;
  
  	tmp[0] = reg;
  	tmp[1] = (len == 1) ? data : (data >> 8);
  	tmp[2] = data;
  	len++;
  	
  	if( i2c_master_send(cl, tmp, len) != len )
  		return -ENODEV;
  	return 0;
  }
  
  static int
  read_reg( struct i2c_client *cl, int reg, int len )
  {
  	u8 buf[2];
  
  	if( len != 1 && len != 2 )
  		return -EINVAL;
  	buf[0] = reg;
  	if( i2c_master_send(cl, buf, 1) != 1 )
  		return -ENODEV;
  	if( i2c_master_recv(cl, buf, len) != len )
  		return -ENODEV;
  	return (len == 2)? ((unsigned int)buf[0] << 8) | buf[1] : buf[0];
  }
  
  static void
  tune_fan( int fan_setting )
  {
  	int val = (fan_setting << 3) | 7;
  
  	/* write_reg( x.fan, 0x24, val, 1 ); */
  	write_reg( x.fan, 0x25, val, 1 );
  	write_reg( x.fan, 0x20, 0, 1 );
  	print_temp("CPU-temp: ", x.temp );
  	if( x.casetemp )
  		print_temp(", Case: ", x.casetemp );
  	printk(",  Fan: %d (tuned %+d)
  ", 11-fan_setting, x.fan_level-fan_setting );
  
  	x.fan_level = fan_setting;
  }
  
  static void
  poll_temp( void )
  {
  	int temp, i, level, casetemp;
  
  	temp = read_reg( x.thermostat, 0, 2 );
  
  	/* this actually occurs when the computer is loaded */
  	if( temp < 0 )
  		return;
  
  	casetemp = read_reg(x.fan, 0x0b, 1) << 8;
  	casetemp |= (read_reg(x.fan, 0x06, 1) & 0x7) << 5;
  
  	if( LOG_TEMP && x.temp != temp ) {
  		print_temp("CPU-temp: ", temp );
  		print_temp(", Case: ", casetemp );
  		printk(",  Fan: %d
  ", 11-x.fan_level );
  	}
  	x.temp = temp;
  	x.casetemp = casetemp;
  
  	level = -1;
  	for( i=0; (temp & 0xffff) > fan_table[i].temp ; i++ )
  		;
  	if( i < x.downind )
  		level = fan_table[i].fan_down_setting;
  	x.downind = i;
  
  	for( i=0; (temp & 0xffff) >= fan_table[i+1].temp ; i++ )
  		;
  	if( x.upind < i )
  		level = fan_table[i].fan_up_setting;
  	x.upind = i;
  
  	if( level >= 0 )
  		tune_fan( level );
  }
  
  
  static void
  setup_hardware( void )
  {
  	int val;
98894dffa   Stephen Rothwell   [POWERPC] therm_w...
216
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
  
  	/* save registers (if we unload the module) */
  	x.r0 = read_reg( x.fan, 0x00, 1 );
  	x.r1 = read_reg( x.fan, 0x01, 1 );
  	x.r20 = read_reg( x.fan, 0x20, 1 );
  	x.r23 = read_reg( x.fan, 0x23, 1 );
  	x.r25 = read_reg( x.fan, 0x25, 1 );
  
  	/* improve measurement resolution (convergence time 1.5s) */
  	if( (val=read_reg(x.thermostat, 1, 1)) >= 0 ) {
  		val |= 0x60;
  		if( write_reg( x.thermostat, 1, val, 1 ) )
  			printk("Failed writing config register
  ");
  	}
  	/* disable interrupts and TAC input */
  	write_reg( x.fan, 0x01, 0x01, 1 );
  	/* enable filter */
  	write_reg( x.fan, 0x23, 0x91, 1 );
  	/* remote temp. controls fan */
  	write_reg( x.fan, 0x00, 0x95, 1 );
  
  	/* The thermostat (which besides measureing temperature controls
  	 * has a THERM output which puts the fan on 100%) is usually
  	 * set to kick in at 80 C (chip default). We reduce this a bit
  	 * to be on the safe side (OSX doesn't)...
  	 */
  	if( x.overheat_temp == (80 << 8) ) {
b8e4a7dae   Lyonel Vincent   powerpc/powermac:...
245
246
  		x.overheat_temp = 75 << 8;
  		x.overheat_hyst = 70 << 8;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
248
249
250
251
252
253
254
255
256
257
258
259
  		write_reg( x.thermostat, 2, x.overheat_hyst, 2 );
  		write_reg( x.thermostat, 3, x.overheat_temp, 2 );
  
  		print_temp("Reducing overheating limit to ", x.overheat_temp );
  		print_temp(" (Hyst: ", x.overheat_hyst );
  		printk(")
  ");
  	}
  
  	/* set an initial fan setting */
  	x.downind = 0xffff;
  	x.upind = -1;
  	/* tune_fan( fan_up_table[x.upind].fan_setting ); */
98894dffa   Stephen Rothwell   [POWERPC] therm_w...
260
261
262
263
264
265
  	err = device_create_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
  	err |= device_create_file( &x.of_dev->dev, &dev_attr_case_temperature );
  	if (err)
  		printk(KERN_WARNING
  			"Failed to create temperature attribute file(s).
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  }
  
  static void
  restore_regs( void )
  {
  	device_remove_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
  	device_remove_file( &x.of_dev->dev, &dev_attr_case_temperature );
  
  	write_reg( x.fan, 0x01, x.r1, 1 );
  	write_reg( x.fan, 0x20, x.r20, 1 );
  	write_reg( x.fan, 0x23, x.r23, 1 );
  	write_reg( x.fan, 0x25, x.r25, 1 );
  	write_reg( x.fan, 0x00, x.r0, 1 );
  }
98f6740ea   Paul Mackerras   [POWERPC] Convert...
280
  static int control_loop(void *dummy)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  {
1baaeea00   Daniel Walker   macintosh/therm_w...
282
  	mutex_lock(&x.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
  	setup_hardware();
1baaeea00   Daniel Walker   macintosh/therm_w...
284
  	mutex_unlock(&x.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285

98f6740ea   Paul Mackerras   [POWERPC] Convert...
286
  	for (;;) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
  		msleep_interruptible(8000);
98f6740ea   Paul Mackerras   [POWERPC] Convert...
288
289
  		if (kthread_should_stop())
  			break;
1baaeea00   Daniel Walker   macintosh/therm_w...
290
  		mutex_lock(&x.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  		poll_temp();
1baaeea00   Daniel Walker   macintosh/therm_w...
292
  		mutex_unlock(&x.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
  	}
1baaeea00   Daniel Walker   macintosh/therm_w...
294
  	mutex_lock(&x.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
  	restore_regs();
1baaeea00   Daniel Walker   macintosh/therm_w...
296
  	mutex_unlock(&x.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297

98f6740ea   Paul Mackerras   [POWERPC] Convert...
298
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
299
300
301
302
303
304
305
306
307
308
  }
  
  
  /************************************************************************/
  /*	i2c probing and setup						*/
  /************************************************************************/
  
  static int
  do_attach( struct i2c_adapter *adapter )
  {
036533e23   Jean Delvare   therm_windtunnel:...
309
310
311
312
313
314
315
316
317
  	/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
  	static const unsigned short scan_ds1775[] = {
  		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
  		I2C_CLIENT_END
  	};
  	static const unsigned short scan_adm1030[] = {
  		0x2c, 0x2d, 0x2e, 0x2f,
  		I2C_CLIENT_END
  	};
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
  
  	if( strncmp(adapter->name, "uni-n", 5) )
  		return 0;
  
  	if( !x.running ) {
036533e23   Jean Delvare   therm_windtunnel:...
323
324
325
326
  		struct i2c_board_info info;
  
  		memset(&info, 0, sizeof(struct i2c_board_info));
  		strlcpy(info.type, "therm_ds1775", I2C_NAME_SIZE);
9a94241af   Jean Delvare   i2c: Add support ...
327
  		i2c_new_probed_device(adapter, &info, scan_ds1775, NULL);
036533e23   Jean Delvare   therm_windtunnel:...
328
329
  
  		strlcpy(info.type, "therm_adm1030", I2C_NAME_SIZE);
9a94241af   Jean Delvare   i2c: Add support ...
330
  		i2c_new_probed_device(adapter, &info, scan_adm1030, NULL);
036533e23   Jean Delvare   therm_windtunnel:...
331

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
333
  		if( x.thermostat && x.fan ) {
  			x.running = 1;
98f6740ea   Paul Mackerras   [POWERPC] Convert...
334
  			x.poll_task = kthread_run(control_loop, NULL, "g4fand");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
336
  		}
  	}
036533e23   Jean Delvare   therm_windtunnel:...
337
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
  }
  
  static int
036533e23   Jean Delvare   therm_windtunnel:...
341
  do_remove(struct i2c_client *client)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
  {
036533e23   Jean Delvare   therm_windtunnel:...
343
344
345
346
  	if (x.running) {
  		x.running = 0;
  		kthread_stop(x.poll_task);
  		x.poll_task = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  	}
036533e23   Jean Delvare   therm_windtunnel:...
348
349
350
351
352
353
354
  	if (client == x.thermostat)
  		x.thermostat = NULL;
  	else if (client == x.fan)
  		x.fan = NULL;
  	else
  		printk(KERN_ERR "g4fan: bad client
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
355

036533e23   Jean Delvare   therm_windtunnel:...
356
357
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
360
361
362
363
364
365
366
367
368
369
  
  static int
  attach_fan( struct i2c_client *cl )
  {
  	if( x.fan )
  		goto out;
  
  	/* check that this is an ADM1030 */
  	if( read_reg(cl, 0x3d, 1) != 0x30 || read_reg(cl, 0x3e, 1) != 0x41 )
  		goto out;
  	printk("ADM1030 fan controller [@%02x]
  ", cl->addr );
036533e23   Jean Delvare   therm_windtunnel:...
370
  	x.fan = cl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
371
   out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
  	return 0;
  }
  
  static int
  attach_thermostat( struct i2c_client *cl ) 
  {
  	int hyst_temp, os_temp, temp;
  
  	if( x.thermostat )
  		goto out;
  
  	if( (temp=read_reg(cl, 0, 2)) < 0 )
  		goto out;
  	
  	/* temperature sanity check */
  	if( temp < 0x1600 || temp > 0x3c00 )
  		goto out;
  	hyst_temp = read_reg(cl, 2, 2);
  	os_temp = read_reg(cl, 3, 2);
  	if( hyst_temp < 0 || os_temp < 0 )
  		goto out;
  
  	printk("DS1775 digital thermometer [@%02x]
  ", cl->addr );
  	print_temp("Temp: ", temp );
  	print_temp("  Hyst: ", hyst_temp );
  	print_temp("  OS: ", os_temp );
  	printk("
  ");
  
  	x.temp = temp;
  	x.overheat_temp = os_temp;
  	x.overheat_hyst = hyst_temp;
036533e23   Jean Delvare   therm_windtunnel:...
405
  	x.thermostat = cl;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
  	return 0;
  }
036533e23   Jean Delvare   therm_windtunnel:...
409
410
411
412
413
414
415
  enum chip { ds1775, adm1030 };
  
  static const struct i2c_device_id therm_windtunnel_id[] = {
  	{ "therm_ds1775", ds1775 },
  	{ "therm_adm1030", adm1030 },
  	{ }
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
416
  static int
036533e23   Jean Delvare   therm_windtunnel:...
417
  do_probe(struct i2c_client *cl, const struct i2c_device_id *id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
  {
036533e23   Jean Delvare   therm_windtunnel:...
419
  	struct i2c_adapter *adapter = cl->adapter;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
421
422
423
  
  	if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA
  				     | I2C_FUNC_SMBUS_WRITE_BYTE) )
  		return 0;
036533e23   Jean Delvare   therm_windtunnel:...
424
425
  	switch (id->driver_data) {
  	case adm1030:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
426
  		return attach_fan( cl );
036533e23   Jean Delvare   therm_windtunnel:...
427
428
429
430
  	case ds1775:
  		return attach_thermostat(cl);
  	}
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  }
036533e23   Jean Delvare   therm_windtunnel:...
432
433
434
435
436
437
438
439
440
  static struct i2c_driver g4fan_driver = {
  	.driver = {
  		.name	= "therm_windtunnel",
  	},
  	.attach_adapter = do_attach,
  	.probe		= do_probe,
  	.remove		= do_remove,
  	.id_table	= therm_windtunnel_id,
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
442
443
444
  
  /************************************************************************/
  /*	initialization / cleanup					*/
  /************************************************************************/
000061245   Grant Likely   dt/powerpc: Elimi...
445
  static int therm_of_probe(struct platform_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
449
450
  {
  	return i2c_add_driver( &g4fan_driver );
  }
  
  static int
2dc115813   Grant Likely   of/device: Replac...
451
  therm_of_remove( struct platform_device *dev )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
452
  {
b3e820968   Jean Delvare   i2c: Make i2c_del...
453
454
  	i2c_del_driver( &g4fan_driver );
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
455
  }
46759a7c1   Márton Németh   powerpc/macintosh...
456
  static const struct of_device_id therm_of_match[] = {{
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457
  	.name		= "fan",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
460
  	.compatible	= "adm1030"
      }, {}
  };
000061245   Grant Likely   dt/powerpc: Elimi...
461
  static struct platform_driver therm_of_driver = {
4018294b5   Grant Likely   of: Remove duplic...
462
463
464
465
466
  	.driver = {
  		.name = "temperature",
  		.owner = THIS_MODULE,
  		.of_match_table = therm_of_match,
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  	.probe		= therm_of_probe,
  	.remove		= therm_of_remove,
  };
  
  struct apple_thermal_info {
  	u8		id;			/* implementation ID */
  	u8		fan_count;		/* number of fans */
  	u8		thermostat_count;	/* number of thermostats */
  	u8		unused;
  };
  
  static int __init
  g4fan_init( void )
  {
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
481
  	const struct apple_thermal_info *info;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
482
  	struct device_node *np;
1baaeea00   Daniel Walker   macintosh/therm_w...
483
  	mutex_init(&x.lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
486
  
  	if( !(np=of_find_node_by_name(NULL, "power-mgt")) )
  		return -ENODEV;
01b2726dd   Stephen Rothwell   [POWERPC] Rename ...
487
  	info = of_get_property(np, "thermal-info", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
488
  	of_node_put(np);
71a157e8e   Grant Likely   of: add 'of_' pre...
489
  	if( !info || !of_machine_is_compatible("PowerMac3,6") )
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
493
494
495
496
497
498
  		return -ENODEV;
  
  	if( info->id != 3 ) {
  		printk(KERN_ERR "therm_windtunnel: unsupported thermal design %d
  ", info->id );
  		return -ENODEV;
  	}
  	if( !(np=of_find_node_by_name(NULL, "fan")) )
  		return -ENODEV;
0365ba7fb   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
499
  	x.of_dev = of_platform_device_create(np, "temperature", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
500
501
502
503
504
505
506
  	of_node_put( np );
  
  	if( !x.of_dev ) {
  		printk(KERN_ERR "Can't register fan controller!
  ");
  		return -ENODEV;
  	}
000061245   Grant Likely   dt/powerpc: Elimi...
507
  	platform_driver_register( &therm_of_driver );
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
512
513
  	return 0;
  }
  
  static void __exit
  g4fan_exit( void )
  {
000061245   Grant Likely   dt/powerpc: Elimi...
514
  	platform_driver_unregister( &therm_of_driver );
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
519
520
521
522
523
524
525
  
  	if( x.of_dev )
  		of_device_unregister( x.of_dev );
  }
  
  module_init(g4fan_init);
  module_exit(g4fan_exit);
  
  MODULE_AUTHOR("Samuel Rydh <samuel@ibrium.se>");
  MODULE_DESCRIPTION("Apple G4 (windtunnel) fan controller");
  MODULE_LICENSE("GPL");