2005-04-17 02:20:36 +04:00
/*
* Device driver for the i2c thermostat found on the iBook G4 , Albook G4
*
* Copyright ( C ) 2003 , 2004 Colin Leroy , Rasmus Rohde , Benjamin Herrenschmidt
*
* Documentation from
* http : //www.analog.com/UploadedFiles/Data_Sheets/115254175ADT7467_pra.pdf
* http : //www.analog.com/UploadedFiles/Data_Sheets/3686221171167ADT7460_b.pdf
*
*/
# 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>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/wait.h>
# include <linux/suspend.h>
# include <linux/kthread.h>
# include <linux/moduleparam.h>
2006-12-07 07:34:23 +03:00
# include <linux/freezer.h>
2008-05-23 10:27:02 +04:00
# include <linux/of_platform.h>
2005-04-17 02:20:36 +04:00
# include <asm/prom.h>
# include <asm/machdep.h>
# include <asm/io.h>
# include <asm/system.h>
# include <asm/sections.h>
# undef DEBUG
# define CONFIG_REG 0x40
# define MANUAL_MASK 0xe0
# define AUTO_MASK 0x20
2009-05-22 14:59:10 +04:00
# define INVERT_MASK 0x10
2005-04-17 02:20:36 +04:00
2005-05-25 23:31:35 +04:00
static u8 TEMP_REG [ 3 ] = { 0x26 , 0x25 , 0x27 } ; /* local, sensor1, sensor2 */
static u8 LIMIT_REG [ 3 ] = { 0x6b , 0x6a , 0x6c } ; /* local, sensor1, sensor2 */
2005-04-17 02:20:36 +04:00
static u8 MANUAL_MODE [ 2 ] = { 0x5c , 0x5d } ;
static u8 REM_CONTROL [ 2 ] = { 0x00 , 0x40 } ;
static u8 FAN_SPEED [ 2 ] = { 0x28 , 0x2a } ;
static u8 FAN_SPD_SET [ 2 ] = { 0x30 , 0x31 } ;
2005-05-25 23:31:35 +04:00
static u8 default_limits_local [ 3 ] = { 70 , 50 , 70 } ; /* local, sensor1, sensor2 */
static u8 default_limits_chip [ 3 ] = { 80 , 65 , 80 } ; /* local, sensor1, sensor2 */
2007-02-10 23:35:12 +03:00
static const char * sensor_location [ 3 ] ;
2005-04-17 02:20:36 +04:00
2007-02-10 23:35:12 +03:00
static int limit_adjust ;
2005-04-17 02:20:36 +04:00
static int fan_speed = - 1 ;
2007-02-10 23:35:12 +03:00
static int verbose ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Colin Leroy <colin@colino.net> " ) ;
MODULE_DESCRIPTION ( " Driver for ADT746x thermostat in iBook G4 and "
" Powerbook G4 Alu " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( limit_adjust , int , 0644 ) ;
2005-05-25 23:31:35 +04:00
MODULE_PARM_DESC ( limit_adjust , " Adjust maximum temperatures (50 sensor1, 70 sensor2) "
2005-04-17 02:20:36 +04:00
" by N degrees. " ) ;
module_param ( fan_speed , int , 0644 ) ;
MODULE_PARM_DESC ( fan_speed , " Specify starting fan speed (0-255) "
" (default 64) " ) ;
2006-01-06 11:11:40 +03:00
module_param ( verbose , bool , 0 ) ;
MODULE_PARM_DESC ( verbose , " Verbose log operations "
" (default 0) " ) ;
2005-04-17 02:20:36 +04:00
struct thermostat {
2009-06-15 20:01:51 +04:00
struct i2c_client * clt ;
2005-04-17 02:20:36 +04:00
u8 temps [ 3 ] ;
u8 cached_temp [ 3 ] ;
u8 initial_limits [ 3 ] ;
u8 limits [ 3 ] ;
int last_speed [ 2 ] ;
int last_var [ 2 ] ;
2009-12-03 19:19:59 +03:00
int pwm_inv [ 2 ] ;
2005-04-17 02:20:36 +04:00
} ;
static enum { ADT7460 , ADT7467 } therm_type ;
static int therm_bus , therm_address ;
static struct of_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 int
write_reg ( struct thermostat * th , int reg , u8 data )
{
u8 tmp [ 2 ] ;
int rc ;
tmp [ 0 ] = reg ;
tmp [ 1 ] = data ;
2009-06-15 20:01:51 +04:00
rc = i2c_master_send ( th - > clt , ( const char * ) tmp , 2 ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
return rc ;
if ( rc ! = 2 )
return - ENODEV ;
return 0 ;
}
static int
read_reg ( struct thermostat * th , int reg )
{
u8 reg_addr , data ;
int rc ;
reg_addr = ( u8 ) reg ;
2009-06-15 20:01:51 +04:00
rc = i2c_master_send ( th - > clt , & reg_addr , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
return rc ;
if ( rc ! = 1 )
return - ENODEV ;
2009-06-15 20:01:51 +04:00
rc = i2c_master_recv ( th - > clt , ( char * ) & data , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 )
return rc ;
return data ;
}
2009-10-05 00:53:46 +04:00
static struct i2c_driver thermostat_driver ;
2005-04-17 02:20:36 +04:00
static int
attach_thermostat ( struct i2c_adapter * adapter )
{
unsigned long bus_no ;
2009-06-15 20:01:51 +04:00
struct i2c_board_info info ;
struct i2c_client * client ;
2005-04-17 02:20:36 +04:00
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 ;
2009-06-15 20:01:51 +04:00
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 .
*/
2009-10-05 00:53:46 +04:00
list_add_tail ( & client - > detected , & thermostat_driver . clients ) ;
2009-06-15 20:01:51 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2009-06-15 20:01:51 +04:00
remove_thermostat ( struct i2c_client * client )
2005-04-17 02:20:36 +04:00
{
2009-06-15 20:01:51 +04:00
struct thermostat * th = i2c_get_clientdata ( client ) ;
2005-04-17 02:20:36 +04:00
int i ;
if ( thread_therm ! = NULL ) {
kthread_stop ( thread_therm ) ;
}
2006-01-06 11:11:40 +03:00
2005-04-17 02:20:36 +04:00
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 ] ) ;
2006-01-06 11:11:40 +03:00
2005-04-17 02:20:36 +04:00
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 ] ;
u16 res ;
/* should start with low byte */
tmp [ 1 ] = read_reg ( th , addr ) ;
tmp [ 0 ] = read_reg ( th , addr + 1 ) ;
res = tmp [ 1 ] + ( tmp [ 0 ] < < 8 ) ;
/* "a value of 0xffff means that the fan has stopped" */
return ( res = = 0xffff ? 0 : ( 90000 * 60 ) / res ) ;
}
static void write_both_fan_speed ( struct thermostat * th , int speed )
{
write_fan_speed ( th , speed , 0 ) ;
if ( therm_type = = ADT7460 )
write_fan_speed ( th , speed , 1 ) ;
}
static void write_fan_speed ( struct thermostat * th , int speed , int fan )
{
u8 manual ;
if ( speed > 0xff )
speed = 0xff ;
else if ( speed < - 1 )
speed = 0 ;
if ( therm_type = = ADT7467 & & fan = = 1 )
return ;
if ( th - > last_speed [ fan ] ! = speed ) {
2006-01-06 11:11:40 +03:00
if ( verbose ) {
if ( speed = = - 1 )
printk ( KERN_DEBUG " adt746x: Setting speed to automatic "
" for %s fan. \n " , sensor_location [ fan + 1 ] ) ;
else
printk ( KERN_DEBUG " adt746x: Setting speed to %d "
" for %s fan. \n " , speed , sensor_location [ fan + 1 ] ) ;
}
2005-04-17 02:20:36 +04:00
} else
return ;
if ( speed > = 0 ) {
manual = read_reg ( th , MANUAL_MODE [ fan ] ) ;
2009-12-03 19:19:59 +03:00
manual & = ~ INVERT_MASK ;
2009-05-22 14:59:10 +04:00
write_reg ( th , MANUAL_MODE [ fan ] ,
2009-12-03 19:19:59 +03:00
manual | MANUAL_MASK | th - > pwm_inv [ fan ] ) ;
2005-04-17 02:20:36 +04:00
write_reg ( th , FAN_SPD_SET [ fan ] , speed ) ;
} else {
/* back to automatic */
if ( therm_type = = ADT7460 ) {
manual = read_reg ( th ,
MANUAL_MODE [ fan ] ) & ( ~ MANUAL_MASK ) ;
2009-12-03 19:19:59 +03:00
manual & = ~ INVERT_MASK ;
manual | = th - > pwm_inv [ fan ] ;
2005-04-17 02:20:36 +04:00
write_reg ( th ,
MANUAL_MODE [ fan ] , manual | REM_CONTROL [ fan ] ) ;
} else {
manual = read_reg ( th , MANUAL_MODE [ fan ] ) ;
2009-12-03 19:19:59 +03:00
manual & = ~ INVERT_MASK ;
manual | = th - > pwm_inv [ fan ] ;
2005-04-17 02:20:36 +04:00
write_reg ( th , MANUAL_MODE [ fan ] , manual & ( ~ AUTO_MASK ) ) ;
}
}
th - > last_speed [ fan ] = speed ;
}
static void read_sensors ( struct thermostat * th )
{
int i = 0 ;
for ( i = 0 ; i < 3 ; i + + )
th - > temps [ i ] = read_reg ( th , TEMP_REG [ i ] ) ;
}
# ifdef DEBUG
static void display_stats ( struct thermostat * th )
{
if ( th - > temps [ 0 ] ! = th - > cached_temp [ 0 ]
| | th - > temps [ 1 ] ! = th - > cached_temp [ 1 ]
| | th - > temps [ 2 ] ! = th - > cached_temp [ 2 ] ) {
printk ( KERN_INFO " adt746x: Temperature infos: "
" thermostats: %d,%d,%d; "
" limits: %d,%d,%d; "
" fan speed: %d RPM \n " ,
th - > temps [ 0 ] , th - > temps [ 1 ] , th - > temps [ 2 ] ,
th - > limits [ 0 ] , th - > limits [ 1 ] , th - > limits [ 2 ] ,
read_fan_speed ( th , FAN_SPEED [ 0 ] ) ) ;
}
th - > cached_temp [ 0 ] = th - > temps [ 0 ] ;
th - > cached_temp [ 1 ] = th - > temps [ 1 ] ;
th - > cached_temp [ 2 ] = th - > temps [ 2 ] ;
}
# endif
static void update_fans_speed ( struct thermostat * th )
{
int lastvar = 0 ; /* last variation, for iBook */
int i = 0 ;
/* 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 var = th - > temps [ i ] - th - > limits [ i ] ;
if ( var > - 1 ) {
int step = ( 255 - fan_speed ) / 7 ;
int new_speed = 0 ;
/* hysteresis : change fan speed only if variation is
* more than two degrees */
if ( abs ( var - th - > last_var [ fan_number ] ) < 2 )
continue ;
started = 1 ;
new_speed = fan_speed + ( ( var - 1 ) * step ) ;
if ( new_speed < fan_speed )
new_speed = fan_speed ;
if ( new_speed > 255 )
new_speed = 255 ;
2006-01-06 11:11:40 +03:00
if ( verbose )
printk ( KERN_DEBUG " adt746x: Setting fans speed to %d "
" (limit exceeded by %d on %s) \n " ,
new_speed , var ,
sensor_location [ fan_number + 1 ] ) ;
2005-04-17 02:20:36 +04:00
write_both_fan_speed ( th , new_speed ) ;
th - > last_var [ fan_number ] = var ;
} else if ( var < - 2 ) {
2005-05-25 23:31:35 +04:00
/* don't stop fan if sensor2 is cold and sensor1 is not
2005-04-17 02:20:36 +04:00
* so cold ( lastvar > = - 1 ) */
if ( i = = 2 & & lastvar < - 1 ) {
if ( th - > last_speed [ fan_number ] ! = 0 )
2006-01-06 11:11:40 +03:00
if ( verbose )
printk ( KERN_DEBUG " adt746x: Stopping "
" fans. \n " ) ;
2005-04-17 02:20:36 +04:00
write_both_fan_speed ( th , 0 ) ;
}
}
lastvar = var ;
if ( started )
return ; /* we don't want to re-stop the fan
2005-05-25 23:31:35 +04:00
* if sensor1 is heating and sensor2 is not */
2005-04-17 02:20:36 +04:00
}
}
static int monitor_task ( void * arg )
{
struct thermostat * th = arg ;
2007-07-17 15:03:35 +04:00
set_freezable ( ) ;
2005-04-17 02:20:36 +04:00
while ( ! kthread_should_stop ( ) ) {
2005-06-25 10:13:50 +04:00
try_to_freeze ( ) ;
2005-04-17 02:20:36 +04:00
msleep_interruptible ( 2000 ) ;
# ifndef DEBUG
if ( fan_speed ! = - 1 )
read_sensors ( th ) ;
# else
read_sensors ( th ) ;
# endif
if ( fan_speed ! = - 1 )
update_fans_speed ( th ) ;
# ifdef DEBUG
display_stats ( th ) ;
# endif
}
return 0 ;
}
static void set_limit ( struct thermostat * th , int i )
{
2005-05-25 23:31:35 +04:00
/* Set sensor1 limit higher to avoid powerdowns */
2005-04-17 02:20:36 +04:00
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 ;
}
2009-06-15 20:01:51 +04:00
static int probe_thermostat ( struct i2c_client * client ,
const struct i2c_device_id * id )
2005-04-17 02:20:36 +04:00
{
struct thermostat * th ;
int rc ;
int i ;
if ( thermostat )
return 0 ;
2007-08-01 02:10:03 +04:00
th = kzalloc ( sizeof ( struct thermostat ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! th )
return - ENOMEM ;
2009-06-15 20:01:51 +04:00
i2c_set_clientdata ( client , th ) ;
th - > clt = client ;
2005-04-17 02:20:36 +04:00
2009-10-14 09:31:32 +04:00
rc = read_reg ( th , CONFIG_REG ) ;
2005-04-17 02:20:36 +04:00
if ( rc < 0 ) {
2009-06-15 20:01:51 +04:00
dev_err ( & client - > dev , " Thermostat failed to read config! \n " ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
}
2006-01-06 11:11:40 +03:00
2005-04-17 02:20:36 +04:00
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 ;
2009-12-03 19:19:59 +03:00
/* 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 ;
2005-04-17 02:20:36 +04:00
/* 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 ;
}
return 0 ;
}
2009-06-15 20:01:51 +04:00
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 ,
} ;
2005-04-17 02:20:36 +04:00
/*
* 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 . . .
*
2005-05-17 14:42:58 +04:00
* FIXME , it does now . . .
2005-04-17 02:20:36 +04:00
*/
# define BUILD_SHOW_FUNC_INT(name, data) \
2005-05-17 14:42:58 +04:00
static ssize_t show_ # # name ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
return sprintf ( buf , " %d \n " , data ) ; \
}
2005-05-25 23:31:35 +04:00
# define BUILD_SHOW_FUNC_STR(name, data) \
2005-05-17 14:42:58 +04:00
static ssize_t show_ # # name ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-05-25 23:31:35 +04:00
{ \
return sprintf ( buf , " %s \n " , data ) ; \
}
2005-04-17 02:20:36 +04:00
# define BUILD_SHOW_FUNC_FAN(name, data) \
2005-05-17 14:42:58 +04:00
static ssize_t show_ # # name ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
return sprintf ( buf , " %d (%d rpm) \n " , \
thermostat - > last_speed [ data ] , \
read_fan_speed ( thermostat , FAN_SPEED [ data ] ) \
) ; \
}
# define BUILD_STORE_FUNC_DEG(name, data) \
2005-05-17 14:42:58 +04:00
static ssize_t store_ # # name ( struct device * dev , struct device_attribute * attr , const char * buf , size_t n ) \
2005-04-17 02:20:36 +04:00
{ \
int val ; \
int i ; \
val = simple_strtol ( buf , NULL , 10 ) ; \
2005-05-25 23:31:35 +04:00
printk ( KERN_INFO " Adjusting limits by %d degrees \n " , val ) ; \
2005-04-17 02:20:36 +04:00
limit_adjust = val ; \
for ( i = 0 ; i < 3 ; i + + ) \
set_limit ( thermostat , i ) ; \
return n ; \
}
# define BUILD_STORE_FUNC_INT(name, data) \
2005-05-17 14:42:58 +04:00
static ssize_t store_ # # name ( struct device * dev , struct device_attribute * attr , const char * buf , size_t n ) \
2005-04-17 02:20:36 +04:00
{ \
2009-01-18 05:03:47 +03:00
int val ; \
val = simple_strtol ( buf , NULL , 10 ) ; \
2005-04-17 02:20:36 +04:00
if ( val < 0 | | val > 255 ) \
return - EINVAL ; \
printk ( KERN_INFO " Setting specified fan speed to %d \n " , val ) ; \
data = val ; \
return n ; \
}
2005-05-25 23:31:35 +04:00
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_STR ( sensor1_location , sensor_location [ 1 ] )
BUILD_SHOW_FUNC_STR ( sensor2_location , sensor_location [ 2 ] )
2005-04-17 02:20:36 +04:00
BUILD_SHOW_FUNC_INT ( specified_fan_speed , fan_speed )
2005-05-25 23:31:35 +04:00
BUILD_SHOW_FUNC_FAN ( sensor1_fan_speed , 0 )
BUILD_SHOW_FUNC_FAN ( sensor2_fan_speed , 1 )
2005-04-17 02:20:36 +04:00
BUILD_STORE_FUNC_INT ( specified_fan_speed , fan_speed )
BUILD_SHOW_FUNC_INT ( limit_adjust , limit_adjust )
BUILD_STORE_FUNC_DEG ( limit_adjust , thermostat )
2005-05-25 23:31:35 +04:00
static DEVICE_ATTR ( sensor1_temperature , S_IRUGO ,
show_sensor1_temperature , NULL ) ;
static DEVICE_ATTR ( sensor2_temperature , S_IRUGO ,
show_sensor2_temperature , NULL ) ;
static DEVICE_ATTR ( sensor1_limit , S_IRUGO ,
show_sensor1_limit , NULL ) ;
static DEVICE_ATTR ( sensor2_limit , S_IRUGO ,
show_sensor2_limit , NULL ) ;
static DEVICE_ATTR ( sensor1_location , S_IRUGO ,
show_sensor1_location , NULL ) ;
static DEVICE_ATTR ( sensor2_location , S_IRUGO ,
show_sensor2_location , NULL ) ;
2005-04-17 02:20:36 +04:00
static DEVICE_ATTR ( specified_fan_speed , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ,
show_specified_fan_speed , store_specified_fan_speed ) ;
2005-05-25 23:31:35 +04:00
static DEVICE_ATTR ( sensor1_fan_speed , S_IRUGO ,
show_sensor1_fan_speed , NULL ) ;
static DEVICE_ATTR ( sensor2_fan_speed , S_IRUGO ,
show_sensor2_fan_speed , NULL ) ;
2005-04-17 02:20:36 +04:00
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 )
{
struct device_node * np ;
2006-07-12 09:40:29 +04:00
const u32 * prop ;
2005-05-25 23:31:35 +04:00
int i = 0 , offset = 0 ;
[POWERPC] therm_adt746x: Eliminate some build warnings
We don't care if the device_create_file calls fail, the driver will work
just as well without them, so just issue a runtime warning.
drivers/macintosh/therm_adt746x.c: In function 'thermostat_init':
drivers/macintosh/therm_adt746x.c:615: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:616: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:617: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:618: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:619: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:620: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:621: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:622: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:623: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:625: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2008-01-03 07:17:12 +03:00
int err ;
2009-01-07 01:41:35 +03:00
2005-04-17 02:20:36 +04:00
np = of_find_node_by_name ( NULL , " fan " ) ;
if ( ! np )
return - ENODEV ;
2007-05-03 11:26:52 +04:00
if ( of_device_is_compatible ( np , " adt7460 " ) )
2005-04-17 02:20:36 +04:00
therm_type = ADT7460 ;
2007-05-03 11:26:52 +04:00
else if ( of_device_is_compatible ( np , " adt7467 " ) )
2005-04-17 02:20:36 +04:00
therm_type = ADT7467 ;
2008-06-09 16:21:51 +04:00
else {
of_node_put ( np ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2008-06-09 16:21:51 +04:00
}
2005-05-25 23:31:34 +04:00
2007-04-27 07:41:15 +04:00
prop = of_get_property ( np , " hwsensor-params-version " , NULL ) ;
2005-05-25 23:31:34 +04:00
printk ( KERN_INFO " adt746x: version %d (%ssupported) \n " , * prop ,
( * prop = = 1 ) ? " " : " un " ) ;
2008-06-09 16:21:51 +04:00
if ( * prop ! = 1 ) {
of_node_put ( np ) ;
2005-05-25 23:31:34 +04:00
return - ENODEV ;
2008-06-09 16:21:51 +04:00
}
2005-04-17 02:20:36 +04:00
2007-04-27 07:41:15 +04:00
prop = of_get_property ( np , " reg " , NULL ) ;
2008-06-09 16:21:51 +04:00
if ( ! prop ) {
of_node_put ( np ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2008-06-09 16:21:51 +04:00
}
2005-04-17 02:20:36 +04:00
/* 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 ;
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 ) ;
2007-04-27 07:41:15 +04:00
if ( of_get_property ( np , " hwsensor-location " , NULL ) ) {
2005-05-25 23:31:35 +04:00
for ( i = 0 ; i < 3 ; i + + ) {
2007-04-27 07:41:15 +04:00
sensor_location [ i ] = of_get_property ( np ,
2005-05-25 23:31:35 +04:00
" hwsensor-location " , NULL ) + offset ;
if ( sensor_location [ i ] = = NULL )
sensor_location [ i ] = " " ;
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 ] = " ? " ;
}
2005-09-23 08:44:06 +04:00
of_dev = of_platform_device_create ( np , " temperatures " , NULL ) ;
2009-01-07 01:41:35 +03:00
of_node_put ( np ) ;
2005-04-17 02:20:36 +04:00
if ( of_dev = = NULL ) {
printk ( KERN_ERR " Can't register temperatures device ! \n " ) ;
return - ENODEV ;
}
2009-01-07 01:41:35 +03:00
[POWERPC] therm_adt746x: Eliminate some build warnings
We don't care if the device_create_file calls fail, the driver will work
just as well without them, so just issue a runtime warning.
drivers/macintosh/therm_adt746x.c: In function 'thermostat_init':
drivers/macintosh/therm_adt746x.c:615: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:616: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:617: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:618: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:619: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:620: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:621: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:622: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:623: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:625: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2008-01-03 07:17:12 +03:00
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 ) ;
2005-04-17 02:20:36 +04:00
if ( therm_type = = ADT7460 )
[POWERPC] therm_adt746x: Eliminate some build warnings
We don't care if the device_create_file calls fail, the driver will work
just as well without them, so just issue a runtime warning.
drivers/macintosh/therm_adt746x.c: In function 'thermostat_init':
drivers/macintosh/therm_adt746x.c:615: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:616: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:617: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:618: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:619: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:620: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:621: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:622: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:623: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
drivers/macintosh/therm_adt746x.c:625: warning: ignoring return value of 'device_create_file', declared with attribute warn_unused_result
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2008-01-03 07:17:12 +03:00
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 " ) ;
2005-04-17 02:20:36 +04:00
2006-04-21 09:04:22 +04:00
# ifndef CONFIG_I2C_POWERMAC
request_module ( " i2c-powermac " ) ;
2005-04-17 02:20:36 +04:00
# endif
return i2c_add_driver ( & thermostat_driver ) ;
}
static void __exit
thermostat_exit ( void )
{
if ( of_dev ) {
2005-05-25 23:31:35 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
device_remove_file ( & of_dev - > dev , & dev_attr_limit_adjust ) ;
device_remove_file ( & of_dev - > dev , & dev_attr_specified_fan_speed ) ;
2005-05-25 23:31:35 +04:00
device_remove_file ( & of_dev - > dev , & dev_attr_sensor1_fan_speed ) ;
2005-04-17 02:20:36 +04:00
if ( therm_type = = ADT7460 )
device_remove_file ( & of_dev - > dev ,
2005-05-25 23:31:35 +04:00
& dev_attr_sensor2_fan_speed ) ;
2005-04-17 02:20:36 +04:00
of_device_unregister ( of_dev ) ;
}
i2c_del_driver ( & thermostat_driver ) ;
}
module_init ( thermostat_init ) ;
module_exit ( thermostat_exit ) ;