2019-05-31 11:09:35 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-02-08 08:42:51 +03:00
/*
* Windfarm PowerMac thermal control . SMU " satellite " controller sensors .
*
* Copyright ( C ) 2005 Paul Mackerras , IBM Corp . < paulus @ samba . org >
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/wait.h>
# include <linux/i2c.h>
2008-05-03 00:34:02 +04:00
# include <linux/mutex.h>
2022-04-01 20:15:53 +03:00
2006-02-08 08:42:51 +03:00
# include <asm/smu.h>
# include <asm/pmac_low_i2c.h>
# include "windfarm.h"
2012-04-19 02:16:46 +04:00
# define VERSION "1.0"
2006-02-08 08:42:51 +03:00
/* If the cache is older than 800ms we'll refetch it */
# define MAX_AGE msecs_to_jiffies(800)
struct wf_sat {
2012-04-19 02:16:46 +04:00
struct kref ref ;
2006-02-08 08:42:51 +03:00
int nr ;
2008-05-03 00:34:02 +04:00
struct mutex mutex ;
2006-02-08 08:42:51 +03:00
unsigned long last_read ; /* jiffies when cache last updated */
u8 cache [ 16 ] ;
2012-04-19 02:16:46 +04:00
struct list_head sensors ;
2009-06-15 20:01:51 +04:00
struct i2c_client * i2c ;
2006-02-08 08:42:51 +03:00
struct device_node * node ;
} ;
static struct wf_sat * sats [ 2 ] ;
struct wf_sat_sensor {
2012-04-19 02:16:46 +04:00
struct list_head link ;
int index ;
int index2 ; /* used for power sensors */
int shift ;
struct wf_sat * sat ;
struct wf_sensor sens ;
2006-02-08 08:42:51 +03:00
} ;
# define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens)
struct smu_sdbp_header * smu_sat_get_sdb_partition ( unsigned int sat_id , int id ,
unsigned int * size )
{
struct wf_sat * sat ;
int err ;
unsigned int i , len ;
u8 * buf ;
u8 data [ 4 ] ;
/* TODO: Add the resulting partition to the device-tree */
if ( sat_id > 1 | | ( sat = sats [ sat_id ] ) = = NULL )
return NULL ;
2009-06-15 20:01:51 +04:00
err = i2c_smbus_write_word_data ( sat - > i2c , 8 , id < < 8 ) ;
2006-02-08 08:42:51 +03:00
if ( err ) {
printk ( KERN_ERR " smu_sat_get_sdb_part wr error %d \n " , err ) ;
return NULL ;
}
2009-06-15 20:01:51 +04:00
err = i2c_smbus_read_word_data ( sat - > i2c , 9 ) ;
2008-11-29 04:17:27 +03:00
if ( err < 0 ) {
2006-02-08 08:42:51 +03:00
printk ( KERN_ERR " smu_sat_get_sdb_part rd len error \n " ) ;
return NULL ;
}
2008-11-29 04:17:27 +03:00
len = err ;
2006-02-08 08:42:51 +03:00
if ( len = = 0 ) {
printk ( KERN_ERR " smu_sat_get_sdb_part no partition %x \n " , id ) ;
return NULL ;
}
len = le16_to_cpu ( len ) ;
len = ( len + 3 ) & ~ 3 ;
buf = kmalloc ( len , GFP_KERNEL ) ;
if ( buf = = NULL )
return NULL ;
for ( i = 0 ; i < len ; i + = 4 ) {
2009-06-15 20:01:51 +04:00
err = i2c_smbus_read_i2c_block_data ( sat - > i2c , 0xa , 4 , data ) ;
i2c: Fix the i2c_smbus_read_i2c_block_data() prototype
Let the drivers specify how many bytes they want to read with
i2c_smbus_read_i2c_block_data(). So far, the block count was
hard-coded to I2C_SMBUS_BLOCK_MAX (32), which did not make much sense.
Many driver authors complained about this before, and I believe it's
about time to fix it. Right now, authors have to do technically stupid
things, such as individual byte reads or full-fledged I2C messaging,
to work around the problem. We do not want to encourage that.
I even found that some bus drivers (e.g. i2c-amd8111) already
implemented I2C block read the "right" way, that is, they didn't
follow the old, broken standard. The fact that it was never noticed
before just shows how little i2c_smbus_read_i2c_block_data() was used,
which isn't that surprising given how broken its prototype was so far.
There are some obvious compatiblity considerations:
* This changes the i2c_smbus_read_i2c_block_data() prototype. Users
outside the kernel tree will notice at compilation time, and will
have to update their code.
* User-space has access to i2c_smbus_xfer() directly using i2c-dev, so
the changed expectations would affect tools such as i2cdump. In order
to preserve binary compatibility, we give I2C_SMBUS_I2C_BLOCK_DATA
a new numeric value, and define I2C_SMBUS_I2C_BLOCK_BROKEN with the
old numeric value. When i2c-dev receives a transaction with the
old value, it can convert it to the new format on the fly.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
2007-07-12 16:12:29 +04:00
if ( err < 0 ) {
2006-02-08 08:42:51 +03:00
printk ( KERN_ERR " smu_sat_get_sdb_part rd err %d \n " ,
err ) ;
goto fail ;
}
buf [ i ] = data [ 1 ] ;
buf [ i + 1 ] = data [ 0 ] ;
buf [ i + 2 ] = data [ 3 ] ;
buf [ i + 3 ] = data [ 2 ] ;
}
2018-10-15 03:18:49 +03:00
printk ( KERN_DEBUG " sat %d partition %x: " , sat_id , id ) ;
print_hex_dump ( KERN_DEBUG , " " , DUMP_PREFIX_OFFSET ,
16 , 1 , buf , len , false ) ;
2006-02-08 08:42:51 +03:00
if ( size )
* size = len ;
return ( struct smu_sdbp_header * ) buf ;
fail :
kfree ( buf ) ;
return NULL ;
}
2006-02-18 00:52:54 +03:00
EXPORT_SYMBOL_GPL ( smu_sat_get_sdb_partition ) ;
2006-02-08 08:42:51 +03:00
/* refresh the cache */
static int wf_sat_read_cache ( struct wf_sat * sat )
{
int err ;
2009-06-15 20:01:51 +04:00
err = i2c_smbus_read_i2c_block_data ( sat - > i2c , 0x3f , 16 , sat - > cache ) ;
i2c: Fix the i2c_smbus_read_i2c_block_data() prototype
Let the drivers specify how many bytes they want to read with
i2c_smbus_read_i2c_block_data(). So far, the block count was
hard-coded to I2C_SMBUS_BLOCK_MAX (32), which did not make much sense.
Many driver authors complained about this before, and I believe it's
about time to fix it. Right now, authors have to do technically stupid
things, such as individual byte reads or full-fledged I2C messaging,
to work around the problem. We do not want to encourage that.
I even found that some bus drivers (e.g. i2c-amd8111) already
implemented I2C block read the "right" way, that is, they didn't
follow the old, broken standard. The fact that it was never noticed
before just shows how little i2c_smbus_read_i2c_block_data() was used,
which isn't that surprising given how broken its prototype was so far.
There are some obvious compatiblity considerations:
* This changes the i2c_smbus_read_i2c_block_data() prototype. Users
outside the kernel tree will notice at compilation time, and will
have to update their code.
* User-space has access to i2c_smbus_xfer() directly using i2c-dev, so
the changed expectations would affect tools such as i2cdump. In order
to preserve binary compatibility, we give I2C_SMBUS_I2C_BLOCK_DATA
a new numeric value, and define I2C_SMBUS_I2C_BLOCK_BROKEN with the
old numeric value. When i2c-dev receives a transaction with the
old value, it can convert it to the new format on the fly.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
2007-07-12 16:12:29 +04:00
if ( err < 0 )
2006-02-08 08:42:51 +03:00
return err ;
sat - > last_read = jiffies ;
2018-10-15 03:18:49 +03:00
2006-02-08 08:42:51 +03:00
# ifdef LOTSA_DEBUG
{
int i ;
2018-10-15 03:18:49 +03:00
printk ( KERN_DEBUG " wf_sat_get: data is " ) ;
print_hex_dump ( KERN_DEBUG , " " , DUMP_PREFIX_OFFSET ,
16 , 1 , sat - > cache , 16 , false ) ;
2006-02-08 08:42:51 +03:00
}
# endif
return 0 ;
}
2012-04-19 02:16:46 +04:00
static int wf_sat_sensor_get ( struct wf_sensor * sr , s32 * value )
2006-02-08 08:42:51 +03:00
{
struct wf_sat_sensor * sens = wf_to_sat ( sr ) ;
struct wf_sat * sat = sens - > sat ;
int i , err ;
s32 val ;
2009-06-15 20:01:51 +04:00
if ( sat - > i2c = = NULL )
2006-02-08 08:42:51 +03:00
return - ENODEV ;
2008-05-03 00:34:02 +04:00
mutex_lock ( & sat - > mutex ) ;
2006-02-08 08:42:51 +03:00
if ( time_after ( jiffies , ( sat - > last_read + MAX_AGE ) ) ) {
err = wf_sat_read_cache ( sat ) ;
if ( err )
goto fail ;
}
i = sens - > index * 2 ;
val = ( ( sat - > cache [ i ] < < 8 ) + sat - > cache [ i + 1 ] ) < < sens - > shift ;
if ( sens - > index2 > = 0 ) {
i = sens - > index2 * 2 ;
/* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */
val = ( val * ( ( sat - > cache [ i ] < < 8 ) + sat - > cache [ i + 1 ] ) ) > > 4 ;
}
* value = val ;
err = 0 ;
fail :
2008-05-03 00:34:02 +04:00
mutex_unlock ( & sat - > mutex ) ;
2006-02-08 08:42:51 +03:00
return err ;
}
2012-04-19 02:16:46 +04:00
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 ;
2023-03-30 06:35:58 +03:00
of_node_put ( sat - > node ) ;
2012-04-19 02:16:46 +04:00
kfree ( sat ) ;
}
static void wf_sat_sensor_release ( struct wf_sensor * sr )
2006-02-08 08:42:51 +03:00
{
struct wf_sat_sensor * sens = wf_to_sat ( sr ) ;
struct wf_sat * sat = sens - > sat ;
kfree ( sens ) ;
2012-04-19 02:16:46 +04:00
kref_put ( & sat - > ref , wf_sat_release ) ;
2006-02-08 08:42:51 +03:00
}
2017-08-03 00:01:45 +03:00
static const struct wf_sensor_ops wf_sat_ops = {
2012-04-19 02:16:46 +04:00
. get_value = wf_sat_sensor_get ,
. release = wf_sat_sensor_release ,
2006-02-08 08:42:51 +03:00
. owner = THIS_MODULE ,
} ;
2022-11-19 01:40:31 +03:00
static int wf_sat_probe ( struct i2c_client * client )
2009-06-15 20:01:51 +04:00
{
2012-05-08 07:28:37 +04:00
struct device_node * dev = client - > dev . of_node ;
2006-02-08 08:42:51 +03:00
struct wf_sat * sat ;
struct wf_sat_sensor * sens ;
2006-07-12 09:40:29 +04:00
const u32 * reg ;
2018-11-17 01:11:01 +03:00
const char * loc ;
2009-06-15 20:01:51 +04:00
u8 chip , core ;
2006-02-08 08:42:51 +03:00
struct device_node * child ;
int shift , cpu , index ;
char * name ;
int vsens [ 2 ] , isens [ 2 ] ;
sat = kzalloc ( sizeof ( struct wf_sat ) , GFP_KERNEL ) ;
if ( sat = = NULL )
2009-06-15 20:01:51 +04:00
return - ENOMEM ;
2006-02-08 08:42:51 +03:00
sat - > nr = - 1 ;
sat - > node = of_node_get ( dev ) ;
2012-04-19 02:16:46 +04:00
kref_init ( & sat - > ref ) ;
2008-05-03 00:34:02 +04:00
mutex_init ( & sat - > mutex ) ;
2009-06-15 20:01:51 +04:00
sat - > i2c = client ;
2012-04-19 02:16:46 +04:00
INIT_LIST_HEAD ( & sat - > sensors ) ;
2009-06-15 20:01:51 +04:00
i2c_set_clientdata ( client , sat ) ;
2006-02-08 08:42:51 +03:00
vsens [ 0 ] = vsens [ 1 ] = - 1 ;
isens [ 0 ] = isens [ 1 ] = - 1 ;
2020-09-14 09:14:11 +03:00
for_each_child_of_node ( dev , child ) {
2007-04-27 07:41:15 +04:00
reg = of_get_property ( child , " reg " , NULL ) ;
loc = of_get_property ( child , " location " , NULL ) ;
2006-02-08 08:42:51 +03:00
if ( reg = = NULL | | loc = = NULL )
continue ;
/* the cooked sensors are between 0x30 and 0x37 */
if ( * reg < 0x30 | | * reg > 0x37 )
continue ;
index = * reg - 0x30 ;
/* expect location to be CPU [AB][01] ... */
if ( strncmp ( loc , " CPU " , 4 ) ! = 0 )
continue ;
chip = loc [ 4 ] - ' A ' ;
core = loc [ 5 ] - ' 0 ' ;
if ( chip > 1 | | core > 1 ) {
printk ( KERN_ERR " wf_sat_create: don't understand "
2017-07-19 00:43:12 +03:00
" location %s for %pOF \n " , loc , child ) ;
2006-02-08 08:42:51 +03:00
continue ;
}
cpu = 2 * chip + core ;
if ( sat - > nr < 0 )
sat - > nr = chip ;
else if ( sat - > nr ! = chip ) {
printk ( KERN_ERR " wf_sat_create: can't cope with "
" multiple CPU chips on one SAT (%s) \n " , loc ) ;
continue ;
}
2018-11-17 01:11:01 +03:00
if ( of_node_is_type ( child , " voltage-sensor " ) ) {
2006-02-08 08:42:51 +03:00
name = " cpu-voltage " ;
shift = 4 ;
vsens [ core ] = index ;
2018-11-17 01:11:01 +03:00
} else if ( of_node_is_type ( child , " current-sensor " ) ) {
2006-02-08 08:42:51 +03:00
name = " cpu-current " ;
shift = 8 ;
isens [ core ] = index ;
2018-11-17 01:11:01 +03:00
} else if ( of_node_is_type ( child , " temp-sensor " ) ) {
2006-02-08 08:42:51 +03:00
name = " cpu-temp " ;
shift = 10 ;
} else
continue ; /* hmmm shouldn't happen */
/* the +16 is enough for "cpu-voltage-n" */
sens = kzalloc ( sizeof ( struct wf_sat_sensor ) + 16 , GFP_KERNEL ) ;
if ( sens = = NULL ) {
printk ( KERN_ERR " wf_sat_create: couldn't create "
" %s sensor %d (no memory) \n " , name , cpu ) ;
continue ;
}
sens - > index = index ;
sens - > index2 = - 1 ;
sens - > shift = shift ;
sens - > sat = sat ;
sens - > sens . ops = & wf_sat_ops ;
sens - > sens . name = ( char * ) ( sens + 1 ) ;
2012-05-03 00:19:43 +04:00
snprintf ( ( char * ) sens - > sens . name , 16 , " %s-%d " , name , cpu ) ;
2006-02-08 08:42:51 +03:00
2012-04-19 02:16:46 +04:00
if ( wf_register_sensor ( & sens - > sens ) )
2006-02-08 08:42:51 +03:00
kfree ( sens ) ;
2012-04-19 02:16:46 +04:00
else {
list_add ( & sens - > link , & sat - > sensors ) ;
kref_get ( & sat - > ref ) ;
2006-02-08 08:42:51 +03:00
}
}
/* make the power sensors */
for ( core = 0 ; core < 2 ; + + core ) {
if ( vsens [ core ] < 0 | | isens [ core ] < 0 )
continue ;
cpu = 2 * sat - > nr + core ;
sens = kzalloc ( sizeof ( struct wf_sat_sensor ) + 16 , GFP_KERNEL ) ;
if ( sens = = NULL ) {
printk ( KERN_ERR " wf_sat_create: couldn't create power "
" sensor %d (no memory) \n " , cpu ) ;
continue ;
}
sens - > index = vsens [ core ] ;
sens - > index2 = isens [ core ] ;
sens - > shift = 0 ;
sens - > sat = sat ;
sens - > sens . ops = & wf_sat_ops ;
sens - > sens . name = ( char * ) ( sens + 1 ) ;
2012-05-03 00:19:43 +04:00
snprintf ( ( char * ) sens - > sens . name , 16 , " cpu-power-%d " , cpu ) ;
2006-02-08 08:42:51 +03:00
2012-04-19 02:16:46 +04:00
if ( wf_register_sensor ( & sens - > sens ) )
2006-02-08 08:42:51 +03:00
kfree ( sens ) ;
2012-04-19 02:16:46 +04:00
else {
list_add ( & sens - > link , & sat - > sensors ) ;
kref_get ( & sat - > ref ) ;
2006-02-08 08:42:51 +03:00
}
}
if ( sat - > nr > = 0 )
sats [ sat - > nr ] = sat ;
2009-06-15 20:01:51 +04:00
return 0 ;
2006-02-08 08:42:51 +03:00
}
2022-08-15 11:02:30 +03:00
static void wf_sat_remove ( struct i2c_client * client )
2006-02-08 08:42:51 +03:00
{
2009-06-15 20:01:51 +04:00
struct wf_sat * sat = i2c_get_clientdata ( client ) ;
2012-04-19 02:16:46 +04:00
struct wf_sat_sensor * sens ;
2006-02-08 08:42:51 +03:00
2012-04-19 02:16:46 +04:00
/* 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 ) ;
}
2009-06-15 20:01:51 +04:00
sat - > i2c = NULL ;
2012-04-19 02:16:46 +04:00
kref_put ( & sat - > ref , wf_sat_release ) ;
2006-02-08 08:42:51 +03:00
}
2009-06-15 20:01:51 +04:00
static const struct i2c_device_id wf_sat_id [ ] = {
2012-04-19 02:16:46 +04:00
{ " MAC,smu-sat " , 0 } ,
2009-06-15 20:01:51 +04:00
{ }
} ;
2012-04-19 02:16:46 +04:00
MODULE_DEVICE_TABLE ( i2c , wf_sat_id ) ;
2009-06-15 20:01:51 +04:00
2020-03-03 15:50:46 +03:00
static const struct of_device_id wf_sat_of_id [ ] = {
{ . compatible = " smu-sat " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , wf_sat_of_id ) ;
2009-06-15 20:01:51 +04:00
static struct i2c_driver wf_sat_driver = {
. driver = {
. name = " wf_smu_sat " ,
2020-03-03 15:50:46 +03:00
. of_match_table = wf_sat_of_id ,
2009-06-15 20:01:51 +04:00
} ,
2023-05-23 22:50:53 +03:00
. probe = wf_sat_probe ,
2009-06-15 20:01:51 +04:00
. remove = wf_sat_remove ,
. id_table = wf_sat_id ,
} ;
2012-10-08 07:00:04 +04:00
module_i2c_driver ( wf_sat_driver ) ;
2006-02-08 08:42:51 +03:00
MODULE_AUTHOR ( " Paul Mackerras <paulus@samba.org> " ) ;
MODULE_DESCRIPTION ( " SMU satellite sensors for PowerMac thermal control " ) ;
MODULE_LICENSE ( " GPL " ) ;