2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
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
*
2010-10-18 13:03:14 +04:00
* Documentation from 115254175 ADT7467_pra . pdf and 3686221171167 ADT7460_b . pdf
2020-07-17 21:29:40 +03:00
* https : //www.onsemi.com/PowerSolutions/product.do?id=ADT7467
* https : //www.onsemi.com/PowerSolutions/product.do?id=ADT7460
2005-04-17 02:20:36 +04:00
*
*/
# 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/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 */
2012-04-19 02:16:43 +04: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 ;
2012-01-13 03:02:20 +04:00
static bool 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 ] ;
2012-04-19 02:16:43 +04:00
struct task_struct * thread ;
struct platform_device * pdev ;
enum {
ADT7460 ,
ADT7467
} type ;
2005-04-17 02:20:36 +04:00
} ;
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 ;
}
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 ) ;
2012-04-19 02:16:43 +04:00
if ( th - > type = = ADT7460 )
2005-04-17 02:20:36 +04:00
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 ;
2012-04-19 02:16:43 +04:00
if ( th - > type = = ADT7467 & & fan = = 1 )
2005-04-17 02:20:36 +04:00
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 */
2012-04-19 02:16:43 +04:00
if ( th - > type = = ADT7460 ) {
2005-04-17 02:20:36 +04:00
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 + + ) {
2018-01-24 04:42:28 +03:00
bool started = false ;
2012-04-19 02:16:43 +04:00
int fan_number = ( th - > type = = ADT7460 & & i = = 2 ) ;
2005-04-17 02:20:36 +04:00
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 ;
2018-01-24 04:42:28 +03:00
started = true ;
2005-04-17 02:20:36 +04:00
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 "
2010-02-06 10:47:20 +03:00
" (limit exceeded by %d on %s) \n " ,
2006-01-06 11:11:40 +03:00
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 )
{
2012-04-19 02:16:43 +04:00
/* 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 ] ) ;
2005-04-17 02:20:36 +04:00
2012-04-19 02:16:43 +04:00
/* set our limits to normal */
th - > limits [ i ] = default_limits_local [ i ] + limit_adjust ;
2005-04-17 02:20:36 +04:00
}
2012-04-19 02:16:43 +04:00
# 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 ) ; \
2005-04-17 02:20:36 +04:00
}
2012-04-19 02:16:43 +04:00
# define BUILD_SHOW_FUNC_INT_LITE(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
{ \
2012-04-19 02:16:43 +04:00
struct thermostat * th = dev_get_drvdata ( dev ) ; \
2005-04-17 02:20:36 +04:00
return sprintf ( buf , " %d (%d rpm) \n " , \
2012-04-19 02:16:43 +04:00
th - > last_speed [ data ] , \
read_fan_speed ( th , FAN_SPEED [ data ] ) \
2005-04-17 02:20:36 +04:00
) ; \
}
# 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
{ \
2012-04-19 02:16:43 +04:00
struct thermostat * th = dev_get_drvdata ( dev ) ; \
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 + + ) \
2012-04-19 02:16:43 +04:00
set_limit ( th , i ) ; \
2005-04-17 02:20:36 +04:00
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 ; \
}
2012-04-19 02:16:43 +04:00
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 ] )
2005-05-25 23:31:35 +04:00
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
2012-04-19 02:16:43 +04:00
BUILD_SHOW_FUNC_INT_LITE ( specified_fan_speed , fan_speed )
BUILD_STORE_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
2012-04-19 02:16:43 +04:00
BUILD_SHOW_FUNC_INT_LITE ( limit_adjust , limit_adjust )
BUILD_STORE_FUNC_DEG ( limit_adjust , th )
2005-04-17 02:20:36 +04:00
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 ) ;
2012-04-19 02:16:43 +04:00
static void thermostat_create_files ( struct thermostat * th )
2005-04-17 02:20:36 +04:00
{
2012-04-19 02:16:43 +04:00
struct device_node * np = th - > clt - > dev . of_node ;
struct device * dev ;
int err ;
2009-01-07 01:41:35 +03:00
2012-04-19 02:16:43 +04:00
/* 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 " ) ;
}
2005-05-25 23:31:34 +04:00
2012-04-19 02:16:43 +04:00
static void thermostat_remove_files ( struct thermostat * th )
{
struct device * dev ;
2005-04-17 02:20:36 +04:00
2012-04-19 02:16:43 +04:00
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 ) ;
2005-04-17 02:20:36 +04:00
2012-04-19 02:16:43 +04:00
}
2005-04-17 02:20:36 +04:00
2012-04-19 02:16:43 +04:00
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 ;
2005-04-17 02:20:36 +04:00
2012-04-19 02:16:43 +04:00
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 ;
2005-04-17 02:20:36 +04:00
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 ;
}
}
2012-04-19 02:16:43 +04:00
th = kzalloc ( sizeof ( struct thermostat ) , GFP_KERNEL ) ;
if ( ! th )
return - ENOMEM ;
i2c_set_clientdata ( client , th ) ;
th - > clt = client ;
th - > type = id - > driver_data ;
2009-01-07 01:41:35 +03:00
2012-04-19 02:16:43 +04:00
rc = read_reg ( th , CONFIG_REG ) ;
if ( rc < 0 ) {
dev_err ( & client - > dev , " Thermostat failed to read config! \n " ) ;
kfree ( th ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2009-01-07 01:41:35 +03:00
2012-04-19 02:16:43 +04:00
/* 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 " ) ;
2010-01-31 07:00:30 +03:00
2012-04-19 02:16:43 +04:00
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 ;
2010-01-31 07:00:30 +03:00
}
2012-04-19 02:16:43 +04:00
static int remove_thermostat ( struct i2c_client * client )
2010-01-31 07:00:30 +03:00
{
2012-04-19 02:16:43 +04:00
struct thermostat * th = i2c_get_clientdata ( client ) ;
int i ;
thermostat_remove_files ( th ) ;
2010-01-31 07:00:30 +03:00
2012-04-19 02:16:43 +04:00
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 ;
2005-04-17 02:20:36 +04:00
}
2012-04-19 02:16:43 +04:00
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 )
2005-04-17 02:20:36 +04:00
{
2012-04-19 02:16:43 +04:00
# ifndef CONFIG_I2C_POWERMAC
request_module ( " i2c-powermac " ) ;
# endif
2005-04-17 02:20:36 +04:00
2012-04-19 02:16:43 +04:00
return i2c_add_driver ( & thermostat_driver ) ;
2010-01-31 07:00:30 +03:00
}
2012-04-19 02:16:43 +04:00
static void __exit thermostat_exit ( void )
2010-01-31 07:00:30 +03:00
{
2005-04-17 02:20:36 +04:00
i2c_del_driver ( & thermostat_driver ) ;
}
module_init ( thermostat_init ) ;
module_exit ( thermostat_exit ) ;