2019-06-03 08:45:02 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-04-19 02:16:53 +04:00
/*
* Windfarm PowerMac thermal control . LM87 sensor
*
* Copyright 2012 Benjamin Herrenschmidt , IBM Corp .
*/
# 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 ) ;
}
2017-08-03 00:01:45 +03:00
static const struct wf_sensor_ops wf_lm87_ops = {
2012-04-19 02:16:53 +04:00
. 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
*/
2018-12-05 22:50:28 +03:00
for_each_child_of_node ( client - > dev . of_node , np ) {
if ( ! of_node_name_eq ( np , " int-temp " ) )
2012-04-19 02:16:53 +04:00
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 ) {
2019-10-18 06:18:32 +03:00
pr_warn ( " wf_lm87: Unsupported sensor %pOF \n " ,
client - > dev . of_node ) ;
2012-04-19 02:16:53 +04:00
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 ) ;
/* 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 ) ;
2020-03-03 15:50:46 +03:00
static const struct of_device_id wf_lm87_of_id [ ] = {
{ . compatible = " lm87cimt " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , wf_lm87_of_id ) ;
2012-04-19 02:16:53 +04:00
static struct i2c_driver wf_lm87_driver = {
. driver = {
. name = " wf_lm87 " ,
2020-03-03 15:50:46 +03:00
. of_match_table = wf_lm87_of_id ,
2012-04-19 02:16:53 +04:00
} ,
. 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 " ) ;