2022-09-10 20:19:42 +03:00
// SPDX-License-Identifier: GPL-2.0-only
# include <linux/bitfield.h>
# include <linux/bits.h>
# include <linux/err.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
# include <linux/i2c.h>
# include <linux/regmap.h>
# include <linux/util_macros.h>
# define REG_CR1 0x00
# define CR1_HYST BIT(5)
# define CR1_DRV GENMASK(4, 3)
# define CR1_TEMP_SRC GENMASK(1, 0)
# define REG_CR2 0x01
# define CR2_STBY BIT(7)
# define CR2_ALERTS BIT(6)
# define CR2_DFC BIT(0)
# define REG_CR3 0x02
# define REG_PWMR 0x50
# define REG_PWMV 0x51
# define REG_STATUS 0x5A
# define STATUS_ALARM_CRIT(ch) BIT(2 + 2 * (ch))
# define STATUS_ALARM_MAX(ch) BIT(3 + 2 * (ch))
# define STATUS_RDFA BIT(6)
# define REG_TACH(ch) (0x52 + (ch) * 2)
# define REG_TEMP_INPUT(ch) (0x56 + (ch) * 2)
# define REG_TEMP_MAX(ch) (0x06 + (ch) * 2)
# define REG_TEMP_CRIT(ch) (0x0A + (ch) * 2)
# define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
# define TEMP11_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
127875 ) , 125 ) * 32 )
# define LUT_SIZE 48
# define REG_LUT(index) (0x20 + (index))
struct max31760_state {
struct regmap * regmap ;
struct lut_attribute {
char name [ 24 ] ;
struct sensor_device_attribute sda ;
} lut [ LUT_SIZE ] ;
struct attribute * attrs [ LUT_SIZE + 2 ] ;
struct attribute_group group ;
const struct attribute_group * groups [ 2 ] ;
} ;
static bool max31760_volatile_reg ( struct device * dev , unsigned int reg )
{
return reg > 0x50 ;
}
static const struct regmap_config regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = 0x5B ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = max31760_volatile_reg ,
} ;
static const int max31760_pwm_freq [ ] = { 33 , 150 , 1500 , 25000 } ;
static int tach_to_rpm ( u16 tach )
{
if ( tach = = 0 )
tach = 1 ;
return 60 * 100000 / tach / 2 ;
}
static int max31760_read ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long * val )
{
struct max31760_state * state = dev_get_drvdata ( dev ) ;
unsigned int regval ;
unsigned int reg_temp ;
s16 temp ;
u8 reg [ 2 ] ;
int ret ;
switch ( type ) {
case hwmon_temp :
switch ( attr ) {
case hwmon_temp_fault :
ret = regmap_read ( state - > regmap , REG_STATUS , & regval ) ;
if ( ret )
return ret ;
* val = FIELD_GET ( STATUS_RDFA , regval ) ;
return 0 ;
case hwmon_temp_max_alarm :
ret = regmap_read ( state - > regmap , REG_STATUS , & regval ) ;
if ( ret )
return ret ;
if ( channel )
* val = FIELD_GET ( STATUS_ALARM_MAX ( 1 ) , regval ) ;
else
* val = FIELD_GET ( STATUS_ALARM_MAX ( 0 ) , regval ) ;
return 0 ;
case hwmon_temp_crit_alarm :
ret = regmap_read ( state - > regmap , REG_STATUS , & regval ) ;
if ( ret )
return ret ;
if ( channel )
* val = FIELD_GET ( STATUS_ALARM_CRIT ( 1 ) , regval ) ;
else
* val = FIELD_GET ( STATUS_ALARM_CRIT ( 0 ) , regval ) ;
return 0 ;
case hwmon_temp_input :
reg_temp = REG_TEMP_INPUT ( channel ) ;
break ;
case hwmon_temp_max :
reg_temp = REG_TEMP_MAX ( channel ) ;
break ;
case hwmon_temp_crit :
reg_temp = REG_TEMP_CRIT ( channel ) ;
break ;
default :
return - EOPNOTSUPP ;
}
ret = regmap_bulk_read ( state - > regmap , reg_temp , reg , 2 ) ;
if ( ret )
return ret ;
temp = ( reg [ 0 ] < < 8 ) | reg [ 1 ] ;
* val = TEMP11_FROM_REG ( temp ) ;
return 0 ;
case hwmon_fan :
switch ( attr ) {
case hwmon_fan_input :
ret = regmap_bulk_read ( state - > regmap , REG_TACH ( channel ) , reg , 2 ) ;
if ( ret )
return ret ;
* val = tach_to_rpm ( reg [ 0 ] * 256 + reg [ 1 ] ) ;
return 0 ;
case hwmon_fan_fault :
ret = regmap_read ( state - > regmap , REG_STATUS , & regval ) ;
if ( ret )
return ret ;
if ( channel )
* val = FIELD_GET ( BIT ( 1 ) , regval ) ;
else
* val = FIELD_GET ( BIT ( 0 ) , regval ) ;
return 0 ;
case hwmon_fan_enable :
ret = regmap_read ( state - > regmap , REG_CR3 , & regval ) ;
if ( ret )
return ret ;
if ( channel )
* val = FIELD_GET ( BIT ( 1 ) , regval ) ;
else
* val = FIELD_GET ( BIT ( 0 ) , regval ) ;
return 0 ;
default :
return - EOPNOTSUPP ;
}
case hwmon_pwm :
switch ( attr ) {
case hwmon_pwm_input :
ret = regmap_read ( state - > regmap , REG_PWMV , & regval ) ;
if ( ret )
return ret ;
* val = regval ;
return 0 ;
case hwmon_pwm_freq :
ret = regmap_read ( state - > regmap , REG_CR1 , & regval ) ;
if ( ret )
return ret ;
regval = FIELD_GET ( CR1_DRV , regval ) ;
if ( regval > = ARRAY_SIZE ( max31760_pwm_freq ) )
return - EINVAL ;
* val = max31760_pwm_freq [ regval ] ;
return 0 ;
case hwmon_pwm_enable :
ret = regmap_read ( state - > regmap , REG_CR2 , & regval ) ;
if ( ret )
return ret ;
* val = 2 - FIELD_GET ( CR2_DFC , regval ) ;
return 0 ;
case hwmon_pwm_auto_channels_temp :
ret = regmap_read ( state - > regmap , REG_CR1 , & regval ) ;
if ( ret )
return ret ;
switch ( FIELD_GET ( CR1_TEMP_SRC , regval ) ) {
case 0 :
* val = 2 ;
break ;
case 1 :
* val = 1 ;
break ;
case 2 :
case 3 :
* val = 3 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
default :
return - EOPNOTSUPP ;
}
default :
return - EOPNOTSUPP ;
}
}
static int max31760_write ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long val )
{
struct max31760_state * state = dev_get_drvdata ( dev ) ;
unsigned int pwm_index ;
unsigned int reg_temp ;
int temp ;
u8 reg_val [ 2 ] ;
switch ( type ) {
case hwmon_temp :
switch ( attr ) {
case hwmon_temp_max :
reg_temp = REG_TEMP_MAX ( channel ) ;
break ;
case hwmon_temp_crit :
reg_temp = REG_TEMP_CRIT ( channel ) ;
break ;
default :
return - EOPNOTSUPP ;
}
temp = TEMP11_TO_REG ( val ) ;
reg_val [ 0 ] = temp > > 8 ;
reg_val [ 1 ] = temp & 0xFF ;
return regmap_bulk_write ( state - > regmap , reg_temp , reg_val , 2 ) ;
case hwmon_fan :
switch ( attr ) {
case hwmon_fan_enable :
if ( val = = 0 )
return regmap_clear_bits ( state - > regmap , REG_CR3 , BIT ( channel ) ) ;
if ( val = = 1 )
return regmap_set_bits ( state - > regmap , REG_CR3 , BIT ( channel ) ) ;
return - EINVAL ;
default :
return - EOPNOTSUPP ;
}
case hwmon_pwm :
switch ( attr ) {
case hwmon_pwm_input :
if ( val < 0 | | val > 255 )
return - EINVAL ;
return regmap_write ( state - > regmap , REG_PWMR , val ) ;
case hwmon_pwm_enable :
if ( val = = 1 )
return regmap_set_bits ( state - > regmap , REG_CR2 , CR2_DFC ) ;
if ( val = = 2 )
return regmap_clear_bits ( state - > regmap , REG_CR2 , CR2_DFC ) ;
return - EINVAL ;
case hwmon_pwm_freq :
pwm_index = find_closest ( val , max31760_pwm_freq ,
ARRAY_SIZE ( max31760_pwm_freq ) ) ;
return regmap_update_bits ( state - > regmap ,
REG_CR1 , CR1_DRV ,
FIELD_PREP ( CR1_DRV , pwm_index ) ) ;
case hwmon_pwm_auto_channels_temp :
switch ( val ) {
case 1 :
break ;
case 2 :
val = 0 ;
break ;
case 3 :
val = 2 ;
break ;
default :
return - EINVAL ;
}
return regmap_update_bits ( state - > regmap , REG_CR1 , CR1_TEMP_SRC , val ) ;
default :
return - EOPNOTSUPP ;
}
default :
return - EOPNOTSUPP ;
}
}
2023-04-06 22:30:32 +02:00
static const struct hwmon_channel_info * const max31760_info [ ] = {
2022-09-10 20:19:42 +03:00
HWMON_CHANNEL_INFO ( chip ,
HWMON_C_REGISTER_TZ ) ,
HWMON_CHANNEL_INFO ( fan ,
HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE ,
HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE ) ,
HWMON_CHANNEL_INFO ( temp ,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT |
HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL ,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL ) ,
HWMON_CHANNEL_INFO ( pwm ,
HWMON_PWM_ENABLE | HWMON_PWM_FREQ | HWMON_PWM_INPUT |
HWMON_PWM_AUTO_CHANNELS_TEMP ) ,
NULL
} ;
static umode_t max31760_is_visible ( const void * data ,
enum hwmon_sensor_types type ,
u32 attr , int channel )
{
switch ( type ) {
case hwmon_temp :
switch ( attr ) {
case hwmon_temp_input :
case hwmon_temp_max_alarm :
case hwmon_temp_crit_alarm :
case hwmon_temp_fault :
case hwmon_temp_label :
return 0444 ;
case hwmon_temp_max :
case hwmon_temp_crit :
return 0644 ;
default :
return 0 ;
}
case hwmon_fan :
switch ( attr ) {
case hwmon_fan_input :
case hwmon_fan_fault :
return 0444 ;
case hwmon_fan_enable :
return 0644 ;
default :
return 0 ;
}
case hwmon_pwm :
switch ( attr ) {
case hwmon_pwm_enable :
case hwmon_pwm_input :
case hwmon_pwm_freq :
case hwmon_pwm_auto_channels_temp :
return 0644 ;
default :
return 0 ;
}
default :
return 0 ;
}
}
static int max31760_read_string ( struct device * dev ,
enum hwmon_sensor_types type ,
u32 attr , int channel , const char * * str )
{
switch ( type ) {
case hwmon_temp :
if ( attr ! = hwmon_temp_label )
return - EOPNOTSUPP ;
* str = channel ? " local " : " remote " ;
return 0 ;
default :
return - EOPNOTSUPP ;
}
}
static const struct hwmon_ops max31760_hwmon_ops = {
. is_visible = max31760_is_visible ,
. read = max31760_read ,
. write = max31760_write ,
. read_string = max31760_read_string
} ;
static const struct hwmon_chip_info max31760_chip_info = {
. ops = & max31760_hwmon_ops ,
. info = max31760_info ,
} ;
static ssize_t lut_show ( struct device * dev ,
struct device_attribute * devattr , char * buf )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( devattr ) ;
struct max31760_state * state = dev_get_drvdata ( dev ) ;
int ret ;
unsigned int regval ;
ret = regmap_read ( state - > regmap , REG_LUT ( sda - > index ) , & regval ) ;
if ( ret )
return ret ;
return sysfs_emit ( buf , " %d \n " , regval ) ;
}
static ssize_t lut_store ( struct device * dev ,
struct device_attribute * devattr ,
const char * buf , size_t count )
{
struct sensor_device_attribute * sda = to_sensor_dev_attr ( devattr ) ;
struct max31760_state * state = dev_get_drvdata ( dev ) ;
int ret ;
u8 pwm ;
ret = kstrtou8 ( buf , 10 , & pwm ) ;
if ( ret )
return ret ;
ret = regmap_write ( state - > regmap , REG_LUT ( sda - > index ) , pwm ) ;
if ( ret )
return ret ;
return count ;
}
static ssize_t pwm1_auto_point_temp_hyst_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
struct max31760_state * state = dev_get_drvdata ( dev ) ;
unsigned int regval ;
int ret ;
ret = regmap_read ( state - > regmap , REG_CR1 , & regval ) ;
if ( ret )
return ret ;
return sysfs_emit ( buf , " %d \n " , ( 1 + ( int ) FIELD_GET ( CR1_HYST , regval ) ) * 2000 ) ;
}
static ssize_t pwm1_auto_point_temp_hyst_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf ,
size_t count )
{
struct max31760_state * state = dev_get_drvdata ( dev ) ;
unsigned int hyst ;
int ret ;
ret = kstrtou32 ( buf , 10 , & hyst ) ;
if ( ret )
return ret ;
if ( hyst < 3000 )
ret = regmap_clear_bits ( state - > regmap , REG_CR1 , CR1_HYST ) ;
else
ret = regmap_set_bits ( state - > regmap , REG_CR1 , CR1_HYST ) ;
if ( ret )
return ret ;
return count ;
}
static DEVICE_ATTR_RW ( pwm1_auto_point_temp_hyst ) ;
static void max31760_create_lut_nodes ( struct max31760_state * state )
{
int i ;
struct sensor_device_attribute * sda ;
struct lut_attribute * lut ;
for ( i = 0 ; i < LUT_SIZE ; + + i ) {
lut = & state - > lut [ i ] ;
sda = & lut - > sda ;
snprintf ( lut - > name , sizeof ( lut - > name ) ,
" pwm1_auto_point%d_pwm " , i + 1 ) ;
sda - > dev_attr . attr . mode = 0644 ;
sda - > index = i ;
sda - > dev_attr . show = lut_show ;
sda - > dev_attr . store = lut_store ;
sda - > dev_attr . attr . name = lut - > name ;
sysfs_attr_init ( & sda - > dev_attr . attr ) ;
state - > attrs [ i ] = & sda - > dev_attr . attr ;
}
state - > attrs [ i ] = & dev_attr_pwm1_auto_point_temp_hyst . attr ;
state - > group . attrs = state - > attrs ;
state - > groups [ 0 ] = & state - > group ;
}
static int max31760_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
struct max31760_state * state ;
struct device * hwmon_dev ;
int ret ;
state = devm_kzalloc ( dev , sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
return - ENOMEM ;
state - > regmap = devm_regmap_init_i2c ( client , & regmap_config ) ;
if ( IS_ERR ( state - > regmap ) )
return dev_err_probe ( dev ,
PTR_ERR ( state - > regmap ) ,
" regmap initialization failed \n " ) ;
dev_set_drvdata ( dev , state ) ;
/* Set alert output to comparator mode */
ret = regmap_set_bits ( state - > regmap , REG_CR2 , CR2_ALERTS ) ;
if ( ret )
return dev_err_probe ( dev , ret , " cannot write register \n " ) ;
max31760_create_lut_nodes ( state ) ;
hwmon_dev = devm_hwmon_device_register_with_info ( dev , client - > name ,
state ,
& max31760_chip_info ,
state - > groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
}
static const struct of_device_id max31760_of_match [ ] = {
{ . compatible = " adi,max31760 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , max31760_of_match ) ;
static const struct i2c_device_id max31760_id [ ] = {
{ " max31760 " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , max31760_id ) ;
static int max31760_suspend ( struct device * dev )
{
struct max31760_state * state = dev_get_drvdata ( dev ) ;
return regmap_set_bits ( state - > regmap , REG_CR2 , CR2_STBY ) ;
}
static int max31760_resume ( struct device * dev )
{
struct max31760_state * state = dev_get_drvdata ( dev ) ;
return regmap_clear_bits ( state - > regmap , REG_CR2 , CR2_STBY ) ;
}
static DEFINE_SIMPLE_DEV_PM_OPS ( max31760_pm_ops , max31760_suspend ,
max31760_resume ) ;
static struct i2c_driver max31760_driver = {
. class = I2C_CLASS_HWMON ,
. driver = {
. name = " max31760 " ,
. of_match_table = max31760_of_match ,
. pm = pm_ptr ( & max31760_pm_ops )
} ,
2023-05-05 15:17:18 +02:00
. probe = max31760_probe ,
2022-09-10 20:19:42 +03:00
. id_table = max31760_id
} ;
module_i2c_driver ( max31760_driver ) ;
MODULE_AUTHOR ( " Ibrahim Tilki <Ibrahim.Tilki@analog.com> " ) ;
MODULE_DESCRIPTION ( " Analog Devices MAX31760 Fan Speed Controller " ) ;
MODULE_SOFTDEP ( " pre: regmap_i2c " ) ;
MODULE_VERSION ( " 1.0 " ) ;
MODULE_LICENSE ( " GPL " ) ;