2011-01-26 20:09:02 -08:00
/*
* Hardware monitoring driver for PMBus devices
*
* Copyright ( c ) 2010 , 2011 Ericsson AB .
2013-01-16 10:31:32 -08:00
* Copyright ( c ) 2012 Guenter Roeck
2011-01-26 20:09:02 -08:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
2012-10-10 15:25:56 +02:00
# include <linux/jiffies.h>
2017-05-21 22:34:43 +02:00
# include <linux/pmbus.h>
2014-10-15 13:55:09 -05:00
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
2011-01-26 20:09:02 -08:00
# include "pmbus.h"
/*
2013-01-20 21:00:01 -08:00
* Number of additional attribute pointers to allocate
* with each call to krealloc
2011-01-26 20:09:02 -08:00
*/
2013-01-20 21:00:01 -08:00
# define PMBUS_ATTR_ALLOC_SIZE 32
2011-01-26 20:09:02 -08:00
/*
* Index into status register array , per status register group
*/
# define PB_STATUS_BASE 0
# define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES)
# define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES)
# define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES)
# define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES)
2013-01-16 10:31:32 -08:00
# define PB_STATUS_TEMP_BASE (PB_STATUS_FAN34_BASE + PMBUS_PAGES)
# define PB_STATUS_INPUT_BASE (PB_STATUS_TEMP_BASE + PMBUS_PAGES)
# define PB_STATUS_VMON_BASE (PB_STATUS_INPUT_BASE + 1)
# define PB_NUM_STATUS_REG (PB_STATUS_VMON_BASE + 1)
2011-01-26 20:09:02 -08:00
2011-07-09 08:58:49 -07:00
# define PMBUS_NAME_SIZE 24
2011-01-26 20:09:02 -08:00
struct pmbus_sensor {
2013-01-20 12:01:41 -08:00
struct pmbus_sensor * next ;
2011-07-09 08:58:49 -07:00
char name [ PMBUS_NAME_SIZE ] ; /* sysfs sensor name */
2013-01-20 12:01:41 -08:00
struct device_attribute attribute ;
2011-01-26 20:09:02 -08:00
u8 page ; /* page number */
2011-07-09 08:30:26 -07:00
u16 reg ; /* register */
2011-01-26 20:09:02 -08:00
enum pmbus_sensor_classes class ; /* sensor class */
bool update ; /* runtime sensor update needed */
int data ; /* Sensor data.
Negative if there was a read error */
} ;
2013-01-20 12:01:41 -08:00
# define to_pmbus_sensor(_attr) \
container_of ( _attr , struct pmbus_sensor , attribute )
2011-01-26 20:09:02 -08:00
struct pmbus_boolean {
2011-07-09 08:58:49 -07:00
char name [ PMBUS_NAME_SIZE ] ; /* sysfs boolean name */
2011-01-26 20:09:02 -08:00
struct sensor_device_attribute attribute ;
2013-01-20 10:05:55 -08:00
struct pmbus_sensor * s1 ;
struct pmbus_sensor * s2 ;
2011-01-26 20:09:02 -08:00
} ;
2013-01-20 10:05:55 -08:00
# define to_pmbus_boolean(_attr) \
container_of ( _attr , struct pmbus_boolean , attribute )
2011-01-26 20:09:02 -08:00
struct pmbus_label {
2011-07-09 08:58:49 -07:00
char name [ PMBUS_NAME_SIZE ] ; /* sysfs label name */
2013-01-20 08:13:21 -08:00
struct device_attribute attribute ;
2011-07-09 08:58:49 -07:00
char label [ PMBUS_NAME_SIZE ] ; /* label */
2011-01-26 20:09:02 -08:00
} ;
2013-01-20 08:13:21 -08:00
# define to_pmbus_label(_attr) \
container_of ( _attr , struct pmbus_label , attribute )
2011-01-26 20:09:02 -08:00
struct pmbus_data {
2013-01-20 08:13:21 -08:00
struct device * dev ;
2011-01-26 20:09:02 -08:00
struct device * hwmon_dev ;
u32 flags ; /* from platform data */
2014-01-30 19:51:14 -08:00
int exponent [ PMBUS_PAGES ] ;
/* linear mode: exponent for output voltages */
2011-01-26 20:09:02 -08:00
const struct pmbus_driver_info * info ;
int max_attributes ;
int num_attributes ;
struct attribute_group group ;
2013-07-06 09:47:08 -07:00
const struct attribute_group * groups [ 2 ] ;
2011-01-26 20:09:02 -08:00
struct pmbus_sensor * sensors ;
struct mutex update_lock ;
bool valid ;
unsigned long last_updated ; /* in jiffies */
/*
* A single status register covers multiple attributes ,
* so we keep them all together .
*/
2017-08-10 16:57:47 -05:00
u16 status [ PB_NUM_STATUS_REG ] ;
2017-08-10 16:57:48 -05:00
2017-08-10 16:57:49 -05:00
bool has_status_word ; /* device uses STATUS_WORD register */
2017-08-10 16:57:48 -05:00
int ( * read_status ) ( struct i2c_client * client , int page ) ;
2011-01-26 20:09:02 -08:00
u8 currpage ;
} ;
2013-01-26 15:15:37 -08:00
void pmbus_clear_cache ( struct i2c_client * client )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
data - > valid = false ;
}
EXPORT_SYMBOL_GPL ( pmbus_clear_cache ) ;
2011-01-26 20:09:02 -08:00
int pmbus_set_page ( struct i2c_client * client , u8 page )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
int rv = 0 ;
int newpage ;
if ( page ! = data - > currpage ) {
rv = i2c_smbus_write_byte_data ( client , PMBUS_PAGE , page ) ;
newpage = i2c_smbus_read_byte_data ( client , PMBUS_PAGE ) ;
if ( newpage ! = page )
2011-09-01 08:34:31 -07:00
rv = - EIO ;
2011-01-26 20:09:02 -08:00
else
data - > currpage = page ;
}
return rv ;
}
EXPORT_SYMBOL_GPL ( pmbus_set_page ) ;
2011-07-08 10:43:57 -07:00
int pmbus_write_byte ( struct i2c_client * client , int page , u8 value )
2011-01-26 20:09:02 -08:00
{
int rv ;
2011-07-09 07:41:01 -07:00
if ( page > = 0 ) {
rv = pmbus_set_page ( client , page ) ;
if ( rv < 0 )
return rv ;
}
2011-01-26 20:09:02 -08:00
return i2c_smbus_write_byte ( client , value ) ;
}
2011-07-08 10:43:57 -07:00
EXPORT_SYMBOL_GPL ( pmbus_write_byte ) ;
2011-01-26 20:09:02 -08:00
2011-07-29 22:08:07 -07:00
/*
* _pmbus_write_byte ( ) is similar to pmbus_write_byte ( ) , but checks if
2013-09-27 14:36:04 +02:00
* a device specific mapping function exists and calls it if necessary .
2011-07-29 22:08:07 -07:00
*/
static int _pmbus_write_byte ( struct i2c_client * client , int page , u8 value )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
const struct pmbus_driver_info * info = data - > info ;
int status ;
if ( info - > write_byte ) {
status = info - > write_byte ( client , page , value ) ;
if ( status ! = - ENODATA )
return status ;
}
return pmbus_write_byte ( client , page , value ) ;
}
2011-07-08 10:41:24 -07:00
int pmbus_write_word_data ( struct i2c_client * client , u8 page , u8 reg , u16 word )
2011-01-26 20:09:02 -08:00
{
int rv ;
rv = pmbus_set_page ( client , page ) ;
if ( rv < 0 )
return rv ;
return i2c_smbus_write_word_data ( client , reg , word ) ;
}
2011-07-08 10:41:24 -07:00
EXPORT_SYMBOL_GPL ( pmbus_write_word_data ) ;
/*
* _pmbus_write_word_data ( ) is similar to pmbus_write_word_data ( ) , but checks if
* a device specific mapping function exists and calls it if necessary .
*/
static int _pmbus_write_word_data ( struct i2c_client * client , int page , int reg ,
u16 word )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
const struct pmbus_driver_info * info = data - > info ;
int status ;
if ( info - > write_word_data ) {
status = info - > write_word_data ( client , page , reg , word ) ;
if ( status ! = - ENODATA )
return status ;
}
if ( reg > = PMBUS_VIRT_BASE )
2011-09-01 08:34:31 -07:00
return - ENXIO ;
2011-07-08 10:41:24 -07:00
return pmbus_write_word_data ( client , page , reg , word ) ;
}
2011-01-26 20:09:02 -08:00
int pmbus_read_word_data ( struct i2c_client * client , u8 page , u8 reg )
{
int rv ;
rv = pmbus_set_page ( client , page ) ;
if ( rv < 0 )
return rv ;
return i2c_smbus_read_word_data ( client , reg ) ;
}
EXPORT_SYMBOL_GPL ( pmbus_read_word_data ) ;
2011-07-08 10:41:24 -07:00
/*
* _pmbus_read_word_data ( ) is similar to pmbus_read_word_data ( ) , but checks if
* a device specific mapping function exists and calls it if necessary .
*/
static int _pmbus_read_word_data ( struct i2c_client * client , int page , int reg )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
const struct pmbus_driver_info * info = data - > info ;
int status ;
if ( info - > read_word_data ) {
status = info - > read_word_data ( client , page , reg ) ;
if ( status ! = - ENODATA )
return status ;
}
2011-07-09 08:30:26 -07:00
if ( reg > = PMBUS_VIRT_BASE )
2011-09-01 08:34:31 -07:00
return - ENXIO ;
2011-07-08 10:41:24 -07:00
return pmbus_read_word_data ( client , page , reg ) ;
}
2011-07-09 07:41:01 -07:00
int pmbus_read_byte_data ( struct i2c_client * client , int page , u8 reg )
2011-01-26 20:09:02 -08:00
{
int rv ;
2011-07-09 07:41:01 -07:00
if ( page > = 0 ) {
rv = pmbus_set_page ( client , page ) ;
if ( rv < 0 )
return rv ;
}
2011-01-26 20:09:02 -08:00
return i2c_smbus_read_byte_data ( client , reg ) ;
}
2011-06-25 11:21:49 -07:00
EXPORT_SYMBOL_GPL ( pmbus_read_byte_data ) ;
2011-01-26 20:09:02 -08:00
2014-10-15 13:55:08 -05:00
int pmbus_write_byte_data ( struct i2c_client * client , int page , u8 reg , u8 value )
{
int rv ;
rv = pmbus_set_page ( client , page ) ;
if ( rv < 0 )
return rv ;
return i2c_smbus_write_byte_data ( client , reg , value ) ;
}
EXPORT_SYMBOL_GPL ( pmbus_write_byte_data ) ;
int pmbus_update_byte_data ( struct i2c_client * client , int page , u8 reg ,
u8 mask , u8 value )
{
unsigned int tmp ;
int rv ;
rv = pmbus_read_byte_data ( client , page , reg ) ;
if ( rv < 0 )
return rv ;
tmp = ( rv & ~ mask ) | ( value & mask ) ;
if ( tmp ! = rv )
rv = pmbus_write_byte_data ( client , page , reg , tmp ) ;
return rv ;
}
EXPORT_SYMBOL_GPL ( pmbus_update_byte_data ) ;
2011-07-09 12:06:45 -07:00
/*
* _pmbus_read_byte_data ( ) is similar to pmbus_read_byte_data ( ) , but checks if
* a device specific mapping function exists and calls it if necessary .
*/
static int _pmbus_read_byte_data ( struct i2c_client * client , int page , int reg )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
const struct pmbus_driver_info * info = data - > info ;
int status ;
if ( info - > read_byte_data ) {
status = info - > read_byte_data ( client , page , reg ) ;
if ( status ! = - ENODATA )
return status ;
}
return pmbus_read_byte_data ( client , page , reg ) ;
}
2011-01-26 20:09:02 -08:00
static void pmbus_clear_fault_page ( struct i2c_client * client , int page )
{
2011-07-29 22:08:07 -07:00
_pmbus_write_byte ( client , page , PMBUS_CLEAR_FAULTS ) ;
2011-01-26 20:09:02 -08:00
}
void pmbus_clear_faults ( struct i2c_client * client )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
int i ;
for ( i = 0 ; i < data - > info - > pages ; i + + )
pmbus_clear_fault_page ( client , i ) ;
}
EXPORT_SYMBOL_GPL ( pmbus_clear_faults ) ;
2011-07-09 07:41:01 -07:00
static int pmbus_check_status_cml ( struct i2c_client * client )
2011-01-26 20:09:02 -08:00
{
2012-05-19 11:35:25 -07:00
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
2011-01-26 20:09:02 -08:00
int status , status2 ;
2017-08-10 16:57:48 -05:00
status = data - > read_status ( client , - 1 ) ;
2011-01-26 20:09:02 -08:00
if ( status < 0 | | ( status & PB_STATUS_CML ) ) {
2011-07-29 22:19:39 -07:00
status2 = _pmbus_read_byte_data ( client , - 1 , PMBUS_STATUS_CML ) ;
2011-01-26 20:09:02 -08:00
if ( status2 < 0 | | ( status2 & PB_CML_FAULT_INVALID_COMMAND ) )
2011-09-01 08:34:31 -07:00
return - EIO ;
2011-01-26 20:09:02 -08:00
}
return 0 ;
}
2013-01-22 16:26:46 -08:00
static bool pmbus_check_register ( struct i2c_client * client ,
int ( * func ) ( struct i2c_client * client ,
int page , int reg ) ,
int page , int reg )
2011-01-26 20:09:02 -08:00
{
int rv ;
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
2013-01-22 16:26:46 -08:00
rv = func ( client , page , reg ) ;
2011-01-26 20:09:02 -08:00
if ( rv > = 0 & & ! ( data - > flags & PMBUS_SKIP_STATUS_CHECK ) )
2011-07-09 07:41:01 -07:00
rv = pmbus_check_status_cml ( client ) ;
pmbus_clear_fault_page ( client , - 1 ) ;
2011-01-26 20:09:02 -08:00
return rv > = 0 ;
}
2013-01-22 16:26:46 -08:00
2017-08-10 16:57:48 -05:00
static bool pmbus_check_status_register ( struct i2c_client * client , int page )
{
int status ;
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
status = data - > read_status ( client , page ) ;
if ( status > = 0 & & ! ( data - > flags & PMBUS_SKIP_STATUS_CHECK ) & &
( status & PB_STATUS_CML ) ) {
status = _pmbus_read_byte_data ( client , - 1 , PMBUS_STATUS_CML ) ;
if ( status < 0 | | ( status & PB_CML_FAULT_INVALID_COMMAND ) )
status = - EIO ;
}
pmbus_clear_fault_page ( client , - 1 ) ;
return status > = 0 ;
}
2013-01-22 16:26:46 -08:00
bool pmbus_check_byte_register ( struct i2c_client * client , int page , int reg )
{
return pmbus_check_register ( client , _pmbus_read_byte_data , page , reg ) ;
}
2011-01-26 20:09:02 -08:00
EXPORT_SYMBOL_GPL ( pmbus_check_byte_register ) ;
bool pmbus_check_word_register ( struct i2c_client * client , int page , int reg )
{
2013-01-22 16:26:46 -08:00
return pmbus_check_register ( client , _pmbus_read_word_data , page , reg ) ;
2011-01-26 20:09:02 -08:00
}
EXPORT_SYMBOL_GPL ( pmbus_check_word_register ) ;
const struct pmbus_driver_info * pmbus_get_driver_info ( struct i2c_client * client )
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
return data - > info ;
}
EXPORT_SYMBOL_GPL ( pmbus_get_driver_info ) ;
2013-01-22 16:26:46 -08:00
static struct _pmbus_status {
u32 func ;
u16 base ;
u16 reg ;
} pmbus_status [ ] = {
{ PMBUS_HAVE_STATUS_VOUT , PB_STATUS_VOUT_BASE , PMBUS_STATUS_VOUT } ,
{ PMBUS_HAVE_STATUS_IOUT , PB_STATUS_IOUT_BASE , PMBUS_STATUS_IOUT } ,
{ PMBUS_HAVE_STATUS_TEMP , PB_STATUS_TEMP_BASE ,
PMBUS_STATUS_TEMPERATURE } ,
{ PMBUS_HAVE_STATUS_FAN12 , PB_STATUS_FAN_BASE , PMBUS_STATUS_FAN_12 } ,
{ PMBUS_HAVE_STATUS_FAN34 , PB_STATUS_FAN34_BASE , PMBUS_STATUS_FAN_34 } ,
} ;
2011-01-26 20:09:02 -08:00
static struct pmbus_data * pmbus_update_device ( struct device * dev )
{
2013-07-06 09:47:08 -07:00
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
2011-01-26 20:09:02 -08:00
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
const struct pmbus_driver_info * info = data - > info ;
2013-01-20 12:01:41 -08:00
struct pmbus_sensor * sensor ;
2011-01-26 20:09:02 -08:00
mutex_lock ( & data - > update_lock ) ;
if ( time_after ( jiffies , data - > last_updated + HZ ) | | ! data - > valid ) {
2013-01-22 16:26:46 -08:00
int i , j ;
2011-01-26 20:09:02 -08:00
2013-01-22 16:26:46 -08:00
for ( i = 0 ; i < info - > pages ; i + + ) {
2011-01-26 20:09:02 -08:00
data - > status [ PB_STATUS_BASE + i ]
2017-08-10 16:57:48 -05:00
= data - > read_status ( client , i ) ;
2013-01-22 16:26:46 -08:00
for ( j = 0 ; j < ARRAY_SIZE ( pmbus_status ) ; j + + ) {
struct _pmbus_status * s = & pmbus_status [ j ] ;
if ( ! ( info - > func [ i ] & s - > func ) )
continue ;
data - > status [ s - > base + i ]
= _pmbus_read_byte_data ( client , i ,
s - > reg ) ;
}
2011-03-05 07:55:10 -08:00
}
2011-01-26 20:09:02 -08:00
if ( info - > func [ 0 ] & PMBUS_HAVE_STATUS_INPUT )
data - > status [ PB_STATUS_INPUT_BASE ]
2011-03-08 23:00:10 -08:00
= _pmbus_read_byte_data ( client , 0 ,
PMBUS_STATUS_INPUT ) ;
2011-01-26 20:09:02 -08:00
2013-01-16 10:31:32 -08:00
if ( info - > func [ 0 ] & PMBUS_HAVE_STATUS_VMON )
data - > status [ PB_STATUS_VMON_BASE ]
= _pmbus_read_byte_data ( client , 0 ,
PMBUS_VIRT_STATUS_VMON ) ;
2013-01-20 12:01:41 -08:00
for ( sensor = data - > sensors ; sensor ; sensor = sensor - > next ) {
2011-01-26 20:09:02 -08:00
if ( ! data - > valid | | sensor - > update )
sensor - > data
2011-07-08 10:41:24 -07:00
= _pmbus_read_word_data ( client ,
sensor - > page ,
sensor - > reg ) ;
2011-01-26 20:09:02 -08:00
}
pmbus_clear_faults ( client ) ;
data - > last_updated = jiffies ;
data - > valid = 1 ;
}
mutex_unlock ( & data - > update_lock ) ;
return data ;
}
/*
* Convert linear sensor values to milli - or micro - units
* depending on sensor type .
*/
2011-07-10 19:31:29 -07:00
static long pmbus_reg2data_linear ( struct pmbus_data * data ,
struct pmbus_sensor * sensor )
2011-01-26 20:09:02 -08:00
{
2011-03-07 18:34:50 -08:00
s16 exponent ;
s32 mantissa ;
2011-01-26 20:09:02 -08:00
long val ;
2011-03-07 18:34:50 -08:00
if ( sensor - > class = = PSC_VOLTAGE_OUT ) { /* LINEAR16 */
2014-01-30 19:51:14 -08:00
exponent = data - > exponent [ sensor - > page ] ;
2011-03-07 18:34:50 -08:00
mantissa = ( u16 ) sensor - > data ;
} else { /* LINEAR11 */
2011-09-28 11:36:20 -07:00
exponent = ( ( s16 ) sensor - > data ) > > 11 ;
mantissa = ( ( s16 ) ( ( sensor - > data & 0x7ff ) < < 5 ) ) > > 5 ;
2011-01-26 20:09:02 -08:00
}
val = mantissa ;
/* scale result to milli-units for all sensors except fans */
if ( sensor - > class ! = PSC_FAN )
val = val * 1000L ;
/* scale result to micro-units for power sensors */
if ( sensor - > class = = PSC_POWER )
val = val * 1000L ;
if ( exponent > = 0 )
val < < = exponent ;
else
val > > = - exponent ;
2011-07-10 19:31:29 -07:00
return val ;
2011-01-26 20:09:02 -08:00
}
/*
* Convert direct sensor values to milli - or micro - units
* depending on sensor type .
*/
2011-07-10 19:31:29 -07:00
static long pmbus_reg2data_direct ( struct pmbus_data * data ,
struct pmbus_sensor * sensor )
2011-01-26 20:09:02 -08:00
{
long val = ( s16 ) sensor - > data ;
long m , b , R ;
m = data - > info - > m [ sensor - > class ] ;
b = data - > info - > b [ sensor - > class ] ;
R = data - > info - > R [ sensor - > class ] ;
if ( m = = 0 )
return 0 ;
/* X = 1/m * (Y * 10^-R - b) */
R = - R ;
/* scale result to milli-units for everything but fans */
if ( sensor - > class ! = PSC_FAN ) {
R + = 3 ;
b * = 1000 ;
}
/* scale result to micro-units for power sensors */
if ( sensor - > class = = PSC_POWER ) {
R + = 3 ;
b * = 1000 ;
}
while ( R > 0 ) {
val * = 10 ;
R - - ;
}
while ( R < 0 ) {
val = DIV_ROUND_CLOSEST ( val , 10 ) ;
R + + ;
}
2011-07-10 19:31:29 -07:00
return ( val - b ) / m ;
2011-01-26 20:09:02 -08:00
}
2011-06-25 11:21:49 -07:00
/*
* Convert VID sensor values to milli - or micro - units
* depending on sensor type .
*/
static long pmbus_reg2data_vid ( struct pmbus_data * data ,
struct pmbus_sensor * sensor )
{
long val = sensor - > data ;
2015-07-20 09:47:33 -07:00
long rv = 0 ;
switch ( data - > info - > vrm_version ) {
case vr11 :
if ( val > = 0x02 & & val < = 0xb2 )
rv = DIV_ROUND_CLOSEST ( 160000 - ( val - 2 ) * 625 , 100 ) ;
break ;
case vr12 :
if ( val > = 0x01 )
rv = 250 + ( val - 1 ) * 5 ;
break ;
}
return rv ;
2011-06-25 11:21:49 -07:00
}
2011-07-10 19:31:29 -07:00
static long pmbus_reg2data ( struct pmbus_data * data , struct pmbus_sensor * sensor )
2011-01-26 20:09:02 -08:00
{
2011-07-10 19:31:29 -07:00
long val ;
2011-01-26 20:09:02 -08:00
2011-06-25 11:21:49 -07:00
switch ( data - > info - > format [ sensor - > class ] ) {
case direct :
2011-01-26 20:09:02 -08:00
val = pmbus_reg2data_direct ( data , sensor ) ;
2011-06-25 11:21:49 -07:00
break ;
case vid :
val = pmbus_reg2data_vid ( data , sensor ) ;
break ;
case linear :
default :
2011-01-26 20:09:02 -08:00
val = pmbus_reg2data_linear ( data , sensor ) ;
2011-06-25 11:21:49 -07:00
break ;
}
2011-01-26 20:09:02 -08:00
return val ;
}
# define MAX_MANTISSA (1023 * 1000)
# define MIN_MANTISSA (511 * 1000)
static u16 pmbus_data2reg_linear ( struct pmbus_data * data ,
2014-01-30 19:51:14 -08:00
struct pmbus_sensor * sensor , long val )
2011-01-26 20:09:02 -08:00
{
2011-03-07 18:34:50 -08:00
s16 exponent = 0 , mantissa ;
2011-01-26 20:09:02 -08:00
bool negative = false ;
/* simple case */
if ( val = = 0 )
return 0 ;
2014-01-30 19:51:14 -08:00
if ( sensor - > class = = PSC_VOLTAGE_OUT ) {
2011-03-07 18:34:50 -08:00
/* LINEAR16 does not support negative voltages */
if ( val < 0 )
return 0 ;
2011-01-26 20:09:02 -08:00
/*
* For a static exponents , we don ' t have a choice
* but to adjust the value to it .
*/
2014-01-30 19:51:14 -08:00
if ( data - > exponent [ sensor - > page ] < 0 )
val < < = - data - > exponent [ sensor - > page ] ;
2011-01-26 20:09:02 -08:00
else
2014-01-30 19:51:14 -08:00
val > > = data - > exponent [ sensor - > page ] ;
2011-01-26 20:09:02 -08:00
val = DIV_ROUND_CLOSEST ( val , 1000 ) ;
2011-03-07 18:34:50 -08:00
return val & 0xffff ;
}
if ( val < 0 ) {
negative = true ;
val = - val ;
2011-01-26 20:09:02 -08:00
}
/* Power is in uW. Convert to mW before converting. */
2014-01-30 19:51:14 -08:00
if ( sensor - > class = = PSC_POWER )
2011-01-26 20:09:02 -08:00
val = DIV_ROUND_CLOSEST ( val , 1000L ) ;
/*
* For simplicity , convert fan data to milli - units
* before calculating the exponent .
*/
2014-01-30 19:51:14 -08:00
if ( sensor - > class = = PSC_FAN )
2011-01-26 20:09:02 -08:00
val = val * 1000 ;
/* Reduce large mantissa until it fits into 10 bit */
while ( val > = MAX_MANTISSA & & exponent < 15 ) {
exponent + + ;
val > > = 1 ;
}
/* Increase small mantissa to improve precision */
while ( val < MIN_MANTISSA & & exponent > - 15 ) {
exponent - - ;
val < < = 1 ;
}
/* Convert mantissa from milli-units to units */
mantissa = DIV_ROUND_CLOSEST ( val , 1000 ) ;
/* Ensure that resulting number is within range */
if ( mantissa > 0x3ff )
mantissa = 0x3ff ;
/* restore sign */
if ( negative )
mantissa = - mantissa ;
/* Convert to 5 bit exponent, 11 bit mantissa */
return ( mantissa & 0x7ff ) | ( ( exponent < < 11 ) & 0xf800 ) ;
}
static u16 pmbus_data2reg_direct ( struct pmbus_data * data ,
2014-01-30 19:51:14 -08:00
struct pmbus_sensor * sensor , long val )
2011-01-26 20:09:02 -08:00
{
long m , b , R ;
2014-01-30 19:51:14 -08:00
m = data - > info - > m [ sensor - > class ] ;
b = data - > info - > b [ sensor - > class ] ;
R = data - > info - > R [ sensor - > class ] ;
2011-01-26 20:09:02 -08:00
/* Power is in uW. Adjust R and b. */
2014-01-30 19:51:14 -08:00
if ( sensor - > class = = PSC_POWER ) {
2011-01-26 20:09:02 -08:00
R - = 3 ;
b * = 1000 ;
}
/* Calculate Y = (m * X + b) * 10^R */
2014-01-30 19:51:14 -08:00
if ( sensor - > class ! = PSC_FAN ) {
2011-01-26 20:09:02 -08:00
R - = 3 ; /* Adjust R and b for data in milli-units */
b * = 1000 ;
}
val = val * m + b ;
while ( R > 0 ) {
val * = 10 ;
R - - ;
}
while ( R < 0 ) {
val = DIV_ROUND_CLOSEST ( val , 10 ) ;
R + + ;
}
return val ;
}
2011-06-25 11:21:49 -07:00
static u16 pmbus_data2reg_vid ( struct pmbus_data * data ,
2014-01-30 19:51:14 -08:00
struct pmbus_sensor * sensor , long val )
2011-06-25 11:21:49 -07:00
{
2013-01-09 08:09:34 -08:00
val = clamp_val ( val , 500 , 1600 ) ;
2011-06-25 11:21:49 -07:00
return 2 + DIV_ROUND_CLOSEST ( ( 1600 - val ) * 100 , 625 ) ;
}
2011-01-26 20:09:02 -08:00
static u16 pmbus_data2reg ( struct pmbus_data * data ,
2014-01-30 19:51:14 -08:00
struct pmbus_sensor * sensor , long val )
2011-01-26 20:09:02 -08:00
{
u16 regval ;
2014-01-30 19:51:14 -08:00
switch ( data - > info - > format [ sensor - > class ] ) {
2011-06-25 11:21:49 -07:00
case direct :
2014-01-30 19:51:14 -08:00
regval = pmbus_data2reg_direct ( data , sensor , val ) ;
2011-06-25 11:21:49 -07:00
break ;
case vid :
2014-01-30 19:51:14 -08:00
regval = pmbus_data2reg_vid ( data , sensor , val ) ;
2011-06-25 11:21:49 -07:00
break ;
case linear :
default :
2014-01-30 19:51:14 -08:00
regval = pmbus_data2reg_linear ( data , sensor , val ) ;
2011-06-25 11:21:49 -07:00
break ;
}
2011-01-26 20:09:02 -08:00
return regval ;
}
/*
* Return boolean calculated from converted data .
2013-01-20 10:05:55 -08:00
* < index > defines a status register index and mask .
* The mask is in the lower 8 bits , the register index is in bits 8. .23 .
2011-01-26 20:09:02 -08:00
*
2013-01-20 10:05:55 -08:00
* The associated pmbus_boolean structure contains optional pointers to two
* sensor attributes . If specified , those attributes are compared against each
* other to determine if a limit has been exceeded .
2011-01-26 20:09:02 -08:00
*
2013-01-20 10:05:55 -08:00
* If the sensor attribute pointers are NULL , the function returns true if
* ( status [ reg ] & mask ) is true .
*
* If sensor attribute pointers are provided , a comparison against a specified
* limit has to be performed to determine the boolean result .
2011-01-26 20:09:02 -08:00
* In this case , the function returns true if v1 > = v2 ( where v1 and v2 are
2013-01-20 10:05:55 -08:00
* sensor values referenced by sensor attribute pointers s1 and s2 ) .
2011-01-26 20:09:02 -08:00
*
* To determine if an object exceeds upper limits , specify < s1 , s2 > = < v , limit > .
* To determine if an object exceeds lower limits , specify < s1 , s2 > = < limit , v > .
*
* If a negative value is stored in any of the referenced registers , this value
* reflects an error code which will be returned .
*/
2013-01-20 10:05:55 -08:00
static int pmbus_get_boolean ( struct pmbus_data * data , struct pmbus_boolean * b ,
int index )
2011-01-26 20:09:02 -08:00
{
2013-01-20 10:05:55 -08:00
struct pmbus_sensor * s1 = b - > s1 ;
struct pmbus_sensor * s2 = b - > s2 ;
2017-08-10 16:57:47 -05:00
u16 reg = ( index > > 16 ) & 0xffff ;
u16 mask = index & 0xffff ;
2012-03-28 09:14:03 -07:00
int ret , status ;
2017-08-10 16:57:47 -05:00
u16 regval ;
2011-01-26 20:09:02 -08:00
status = data - > status [ reg ] ;
if ( status < 0 )
return status ;
regval = status & mask ;
2013-01-20 10:05:55 -08:00
if ( ! s1 & & ! s2 ) {
2012-03-28 09:14:03 -07:00
ret = ! ! regval ;
2013-01-20 10:05:55 -08:00
} else if ( ! s1 | | ! s2 ) {
2013-09-13 10:31:38 -07:00
WARN ( 1 , " Bad boolean descriptor %p: s1=%p, s2=%p \n " , b , s1 , s2 ) ;
2013-01-20 10:05:55 -08:00
return 0 ;
} else {
2011-07-10 19:31:29 -07:00
long v1 , v2 ;
2011-01-26 20:09:02 -08:00
2013-01-20 10:05:55 -08:00
if ( s1 - > data < 0 )
return s1 - > data ;
if ( s2 - > data < 0 )
return s2 - > data ;
2011-01-26 20:09:02 -08:00
2013-01-20 10:05:55 -08:00
v1 = pmbus_reg2data ( data , s1 ) ;
v2 = pmbus_reg2data ( data , s2 ) ;
2012-03-28 09:14:03 -07:00
ret = ! ! ( regval & & v1 > = v2 ) ;
2011-01-26 20:09:02 -08:00
}
2012-03-28 09:14:03 -07:00
return ret ;
2011-01-26 20:09:02 -08:00
}
static ssize_t pmbus_show_boolean ( struct device * dev ,
struct device_attribute * da , char * buf )
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2013-01-20 10:05:55 -08:00
struct pmbus_boolean * boolean = to_pmbus_boolean ( attr ) ;
2011-01-26 20:09:02 -08:00
struct pmbus_data * data = pmbus_update_device ( dev ) ;
int val ;
2013-01-20 10:05:55 -08:00
val = pmbus_get_boolean ( data , boolean , attr - > index ) ;
2012-03-28 09:14:03 -07:00
if ( val < 0 )
return val ;
2011-01-26 20:09:02 -08:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , val ) ;
}
static ssize_t pmbus_show_sensor ( struct device * dev ,
2013-01-20 12:01:41 -08:00
struct device_attribute * devattr , char * buf )
2011-01-26 20:09:02 -08:00
{
struct pmbus_data * data = pmbus_update_device ( dev ) ;
2013-01-20 12:01:41 -08:00
struct pmbus_sensor * sensor = to_pmbus_sensor ( devattr ) ;
2011-01-26 20:09:02 -08:00
if ( sensor - > data < 0 )
return sensor - > data ;
2011-07-10 19:31:29 -07:00
return snprintf ( buf , PAGE_SIZE , " %ld \n " , pmbus_reg2data ( data , sensor ) ) ;
2011-01-26 20:09:02 -08:00
}
static ssize_t pmbus_set_sensor ( struct device * dev ,
struct device_attribute * devattr ,
const char * buf , size_t count )
{
2013-07-06 09:47:08 -07:00
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
2011-01-26 20:09:02 -08:00
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
2013-01-20 12:01:41 -08:00
struct pmbus_sensor * sensor = to_pmbus_sensor ( devattr ) ;
2011-01-26 20:09:02 -08:00
ssize_t rv = count ;
long val = 0 ;
int ret ;
u16 regval ;
2012-01-16 10:14:54 -08:00
if ( kstrtol ( buf , 10 , & val ) < 0 )
2011-01-26 20:09:02 -08:00
return - EINVAL ;
mutex_lock ( & data - > update_lock ) ;
2014-01-30 19:51:14 -08:00
regval = pmbus_data2reg ( data , sensor , val ) ;
2011-07-08 10:41:24 -07:00
ret = _pmbus_write_word_data ( client , sensor - > page , sensor - > reg , regval ) ;
2011-01-26 20:09:02 -08:00
if ( ret < 0 )
rv = ret ;
else
2013-01-20 12:01:41 -08:00
sensor - > data = regval ;
2011-01-26 20:09:02 -08:00
mutex_unlock ( & data - > update_lock ) ;
return rv ;
}
static ssize_t pmbus_show_label ( struct device * dev ,
struct device_attribute * da , char * buf )
{
2013-01-20 08:13:21 -08:00
struct pmbus_label * label = to_pmbus_label ( da ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , label - > label ) ;
}
2011-01-26 20:09:02 -08:00
2013-01-20 21:00:01 -08:00
static int pmbus_add_attribute ( struct pmbus_data * data , struct attribute * attr )
{
if ( data - > num_attributes > = data - > max_attributes - 1 ) {
2013-03-14 13:30:20 +00:00
int new_max_attrs = data - > max_attributes + PMBUS_ATTR_ALLOC_SIZE ;
void * new_attrs = krealloc ( data - > group . attrs ,
new_max_attrs * sizeof ( void * ) ,
GFP_KERNEL ) ;
if ( ! new_attrs )
2013-01-20 21:00:01 -08:00
return - ENOMEM ;
2013-03-14 13:30:20 +00:00
data - > group . attrs = new_attrs ;
data - > max_attributes = new_max_attrs ;
2013-01-20 21:00:01 -08:00
}
data - > group . attrs [ data - > num_attributes + + ] = attr ;
data - > group . attrs [ data - > num_attributes ] = NULL ;
return 0 ;
}
2013-01-20 08:13:21 -08:00
static void pmbus_dev_attr_init ( struct device_attribute * dev_attr ,
const char * name ,
umode_t mode ,
ssize_t ( * show ) ( struct device * dev ,
struct device_attribute * attr ,
char * buf ) ,
ssize_t ( * store ) ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count ) )
{
sysfs_attr_init ( & dev_attr - > attr ) ;
dev_attr - > attr . name = name ;
dev_attr - > attr . mode = mode ;
dev_attr - > show = show ;
dev_attr - > store = store ;
2011-01-26 20:09:02 -08:00
}
2013-01-19 20:59:04 -08:00
static void pmbus_attr_init ( struct sensor_device_attribute * a ,
const char * name ,
umode_t mode ,
ssize_t ( * show ) ( struct device * dev ,
struct device_attribute * attr ,
char * buf ) ,
ssize_t ( * store ) ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count ) ,
int idx )
{
2013-01-20 08:13:21 -08:00
pmbus_dev_attr_init ( & a - > dev_attr , name , mode , show , store ) ;
2013-01-19 20:59:04 -08:00
a - > index = idx ;
}
2011-01-26 20:09:02 -08:00
2013-01-20 08:13:21 -08:00
static int pmbus_add_boolean ( struct pmbus_data * data ,
const char * name , const char * type , int seq ,
2013-01-20 10:05:55 -08:00
struct pmbus_sensor * s1 ,
struct pmbus_sensor * s2 ,
2017-08-10 16:57:47 -05:00
u16 reg , u16 mask )
2011-01-26 20:09:02 -08:00
{
struct pmbus_boolean * boolean ;
2013-01-19 20:59:04 -08:00
struct sensor_device_attribute * a ;
2011-01-26 20:09:02 -08:00
2013-01-20 08:13:21 -08:00
boolean = devm_kzalloc ( data - > dev , sizeof ( * boolean ) , GFP_KERNEL ) ;
if ( ! boolean )
return - ENOMEM ;
2011-01-26 20:09:02 -08:00
2013-01-19 20:59:04 -08:00
a = & boolean - > attribute ;
2011-01-26 20:09:02 -08:00
snprintf ( boolean - > name , sizeof ( boolean - > name ) , " %s%d_%s " ,
name , seq , type ) ;
2013-01-20 10:05:55 -08:00
boolean - > s1 = s1 ;
boolean - > s2 = s2 ;
2013-01-19 20:59:04 -08:00
pmbus_attr_init ( a , boolean - > name , S_IRUGO , pmbus_show_boolean , NULL ,
2017-08-10 16:57:47 -05:00
( reg < < 16 ) | mask ) ;
2013-01-20 08:13:21 -08:00
2013-01-20 21:00:01 -08:00
return pmbus_add_attribute ( data , & a - > dev_attr . attr ) ;
2011-01-26 20:09:02 -08:00
}
2013-01-20 10:05:55 -08:00
static struct pmbus_sensor * pmbus_add_sensor ( struct pmbus_data * data ,
const char * name , const char * type ,
int seq , int page , int reg ,
enum pmbus_sensor_classes class ,
bool update , bool readonly )
2011-01-26 20:09:02 -08:00
{
struct pmbus_sensor * sensor ;
2013-01-20 12:01:41 -08:00
struct device_attribute * a ;
2011-01-26 20:09:02 -08:00
2013-01-20 12:01:41 -08:00
sensor = devm_kzalloc ( data - > dev , sizeof ( * sensor ) , GFP_KERNEL ) ;
if ( ! sensor )
return NULL ;
2013-01-19 20:59:04 -08:00
a = & sensor - > attribute ;
2011-01-26 20:09:02 -08:00
snprintf ( sensor - > name , sizeof ( sensor - > name ) , " %s%d_%s " ,
name , seq , type ) ;
sensor - > page = page ;
sensor - > reg = reg ;
sensor - > class = class ;
sensor - > update = update ;
2013-01-20 12:01:41 -08:00
pmbus_dev_attr_init ( a , sensor - > name ,
readonly ? S_IRUGO : S_IRUGO | S_IWUSR ,
pmbus_show_sensor , pmbus_set_sensor ) ;
2013-01-19 20:59:04 -08:00
2013-01-20 21:00:01 -08:00
if ( pmbus_add_attribute ( data , & a - > attr ) )
return NULL ;
2013-01-20 12:01:41 -08:00
sensor - > next = data - > sensors ;
data - > sensors = sensor ;
2013-01-20 10:05:55 -08:00
return sensor ;
2011-01-26 20:09:02 -08:00
}
2013-01-20 08:13:21 -08:00
static int pmbus_add_label ( struct pmbus_data * data ,
const char * name , int seq ,
const char * lstring , int index )
2011-01-26 20:09:02 -08:00
{
struct pmbus_label * label ;
2013-01-20 08:13:21 -08:00
struct device_attribute * a ;
2011-01-26 20:09:02 -08:00
2013-01-20 08:13:21 -08:00
label = devm_kzalloc ( data - > dev , sizeof ( * label ) , GFP_KERNEL ) ;
if ( ! label )
return - ENOMEM ;
2011-01-26 20:09:02 -08:00
2013-01-19 20:59:04 -08:00
a = & label - > attribute ;
2011-01-26 20:09:02 -08:00
snprintf ( label - > name , sizeof ( label - > name ) , " %s%d_label " , name , seq ) ;
if ( ! index )
strncpy ( label - > label , lstring , sizeof ( label - > label ) - 1 ) ;
else
snprintf ( label - > label , sizeof ( label - > label ) , " %s%d " , lstring ,
index ) ;
2013-01-20 08:13:21 -08:00
pmbus_dev_attr_init ( a , label - > name , S_IRUGO , pmbus_show_label , NULL ) ;
2013-01-20 21:00:01 -08:00
return pmbus_add_attribute ( data , & a - > attr ) ;
2011-01-26 20:09:02 -08:00
}
/*
* Search for attributes . Allocate sensors , booleans , and labels as needed .
*/
2011-03-11 18:09:33 -08:00
/*
* The pmbus_limit_attr structure describes a single limit attribute
* and its associated alarm attribute .
*/
struct pmbus_limit_attr {
2011-07-09 08:30:26 -07:00
u16 reg ; /* Limit register */
2013-01-22 16:26:46 -08:00
u16 sbit ; /* Alarm attribute status bit */
2011-07-09 08:30:26 -07:00
bool update ; /* True if register needs updates */
2011-09-10 06:02:12 -07:00
bool low ; /* True if low limit; for limits with compare
functions only */
2011-03-11 18:09:33 -08:00
const char * attr ; /* Attribute name */
const char * alarm ; /* Alarm attribute name */
} ;
/*
* The pmbus_sensor_attr structure describes one sensor attribute . This
* description includes a reference to the associated limit attributes .
*/
struct pmbus_sensor_attr {
2013-01-16 10:31:32 -08:00
u16 reg ; /* sensor register */
2017-08-10 16:57:47 -05:00
u16 gbit ; /* generic status bit */
2013-01-22 16:26:46 -08:00
u8 nlimit ; /* # of limit registers */
2011-03-11 18:09:33 -08:00
enum pmbus_sensor_classes class ; /* sensor class */
const char * label ; /* sensor label */
bool paged ; /* true if paged sensor */
bool update ; /* true if update needed */
bool compare ; /* true if compare function needed */
u32 func ; /* sensor mask */
u32 sfunc ; /* sensor status mask */
int sbase ; /* status base register */
const struct pmbus_limit_attr * limit ; /* limit registers */
} ;
/*
* Add a set of limit attributes and , if supported , the associated
* alarm attributes .
2013-01-20 08:13:21 -08:00
* returns 0 if no alarm register found , 1 if an alarm register was found ,
* < 0 on errors .
2011-03-11 18:09:33 -08:00
*/
2013-01-20 08:13:21 -08:00
static int pmbus_add_limit_attrs ( struct i2c_client * client ,
struct pmbus_data * data ,
const struct pmbus_driver_info * info ,
const char * name , int index , int page ,
2013-01-20 10:05:55 -08:00
struct pmbus_sensor * base ,
2013-01-20 08:13:21 -08:00
const struct pmbus_sensor_attr * attr )
2011-03-11 18:09:33 -08:00
{
const struct pmbus_limit_attr * l = attr - > limit ;
int nlimit = attr - > nlimit ;
2013-01-20 08:13:21 -08:00
int have_alarm = 0 ;
2013-01-20 10:05:55 -08:00
int i , ret ;
struct pmbus_sensor * curr ;
2011-03-11 18:09:33 -08:00
for ( i = 0 ; i < nlimit ; i + + ) {
if ( pmbus_check_word_register ( client , page , l - > reg ) ) {
2013-01-20 10:05:55 -08:00
curr = pmbus_add_sensor ( data , name , l - > attr , index ,
page , l - > reg , attr - > class ,
attr - > update | | l - > update ,
false ) ;
2013-01-20 12:01:41 -08:00
if ( ! curr )
return - ENOMEM ;
2011-07-09 08:30:26 -07:00
if ( l - > sbit & & ( info - > func [ page ] & attr - > sfunc ) ) {
2013-01-20 10:05:55 -08:00
ret = pmbus_add_boolean ( data , name ,
l - > alarm , index ,
attr - > compare ? l - > low ? curr : base
: NULL ,
attr - > compare ? l - > low ? base : curr
: NULL ,
attr - > sbase + page , l - > sbit ) ;
if ( ret )
return ret ;
2013-01-20 08:13:21 -08:00
have_alarm = 1 ;
2011-01-26 20:09:02 -08:00
}
}
2011-03-11 18:09:33 -08:00
l + + ;
2011-01-26 20:09:02 -08:00
}
2011-03-11 18:09:33 -08:00
return have_alarm ;
}
2011-01-26 20:09:02 -08:00
2013-01-20 08:13:21 -08:00
static int pmbus_add_sensor_attrs_one ( struct i2c_client * client ,
struct pmbus_data * data ,
const struct pmbus_driver_info * info ,
const char * name ,
int index , int page ,
const struct pmbus_sensor_attr * attr )
2011-03-11 18:09:33 -08:00
{
2013-01-20 10:05:55 -08:00
struct pmbus_sensor * base ;
2017-08-10 16:57:49 -05:00
bool upper = ! ! ( attr - > gbit & 0xff00 ) ; /* need to check STATUS_WORD */
2013-01-20 08:13:21 -08:00
int ret ;
2011-03-11 18:09:33 -08:00
2013-01-20 08:13:21 -08:00
if ( attr - > label ) {
ret = pmbus_add_label ( data , name , index , attr - > label ,
attr - > paged ? page + 1 : 0 ) ;
if ( ret )
return ret ;
}
2013-01-20 10:05:55 -08:00
base = pmbus_add_sensor ( data , name , " input " , index , page , attr - > reg ,
attr - > class , true , true ) ;
2013-01-20 12:01:41 -08:00
if ( ! base )
return - ENOMEM ;
2011-03-11 18:09:33 -08:00
if ( attr - > sfunc ) {
2013-01-20 08:13:21 -08:00
ret = pmbus_add_limit_attrs ( client , data , info , name ,
2013-01-20 10:05:55 -08:00
index , page , base , attr ) ;
2013-01-20 08:13:21 -08:00
if ( ret < 0 )
return ret ;
2011-01-26 20:09:02 -08:00
/*
* Add generic alarm attribute only if there are no individual
2011-07-09 12:06:45 -07:00
* alarm attributes , if there is a global alarm bit , and if
2017-08-10 16:57:49 -05:00
* the generic status register ( word or byte , depending on
* which global bit is set ) for this page is accessible .
2011-01-26 20:09:02 -08:00
*/
2013-01-20 08:13:21 -08:00
if ( ! ret & & attr - > gbit & &
2017-08-10 16:57:49 -05:00
( ! upper | | ( upper & & data - > has_status_word ) ) & &
2017-08-10 16:57:48 -05:00
pmbus_check_status_register ( client , page ) ) {
2013-01-20 10:05:55 -08:00
ret = pmbus_add_boolean ( data , name , " alarm " , index ,
NULL , NULL ,
PB_STATUS_BASE + page ,
attr - > gbit ) ;
2013-01-20 08:13:21 -08:00
if ( ret )
return ret ;
}
2011-01-26 20:09:02 -08:00
}
2013-01-20 08:13:21 -08:00
return 0 ;
2011-03-11 18:09:33 -08:00
}
2011-01-26 20:09:02 -08:00
2013-01-20 08:13:21 -08:00
static int pmbus_add_sensor_attrs ( struct i2c_client * client ,
struct pmbus_data * data ,
const char * name ,
const struct pmbus_sensor_attr * attrs ,
int nattrs )
2011-03-11 18:09:33 -08:00
{
const struct pmbus_driver_info * info = data - > info ;
int index , i ;
2013-01-20 08:13:21 -08:00
int ret ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
index = 1 ;
for ( i = 0 ; i < nattrs ; i + + ) {
int page , pages ;
pages = attrs - > paged ? info - > pages : 1 ;
for ( page = 0 ; page < pages ; page + + ) {
if ( ! ( info - > func [ page ] & attrs - > func ) )
continue ;
2013-01-20 08:13:21 -08:00
ret = pmbus_add_sensor_attrs_one ( client , data , info ,
name , index , page ,
attrs ) ;
if ( ret )
return ret ;
2011-03-11 18:09:33 -08:00
index + + ;
2011-01-26 20:09:02 -08:00
}
2011-03-11 18:09:33 -08:00
attrs + + ;
2011-01-26 20:09:02 -08:00
}
2013-01-20 08:13:21 -08:00
return 0 ;
2011-03-11 18:09:33 -08:00
}
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_limit_attr vin_limit_attrs [ ] = {
{
. reg = PMBUS_VIN_UV_WARN_LIMIT ,
. attr = " min " ,
. alarm = " min_alarm " ,
. sbit = PB_VOLTAGE_UV_WARNING ,
} , {
. reg = PMBUS_VIN_UV_FAULT_LIMIT ,
. attr = " lcrit " ,
. alarm = " lcrit_alarm " ,
. sbit = PB_VOLTAGE_UV_FAULT ,
} , {
. reg = PMBUS_VIN_OV_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_VOLTAGE_OV_WARNING ,
} , {
. reg = PMBUS_VIN_OV_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_VOLTAGE_OV_FAULT ,
2011-07-09 08:30:26 -07:00
} , {
. reg = PMBUS_VIRT_READ_VIN_AVG ,
. update = true ,
. attr = " average " ,
} , {
. reg = PMBUS_VIRT_READ_VIN_MIN ,
. update = true ,
. attr = " lowest " ,
} , {
. reg = PMBUS_VIRT_READ_VIN_MAX ,
. update = true ,
. attr = " highest " ,
} , {
. reg = PMBUS_VIRT_RESET_VIN_HISTORY ,
. attr = " reset_history " ,
2011-03-11 18:09:33 -08:00
} ,
} ;
2013-01-16 10:31:32 -08:00
static const struct pmbus_limit_attr vmon_limit_attrs [ ] = {
{
. reg = PMBUS_VIRT_VMON_UV_WARN_LIMIT ,
. attr = " min " ,
. alarm = " min_alarm " ,
. sbit = PB_VOLTAGE_UV_WARNING ,
} , {
. reg = PMBUS_VIRT_VMON_UV_FAULT_LIMIT ,
. attr = " lcrit " ,
. alarm = " lcrit_alarm " ,
. sbit = PB_VOLTAGE_UV_FAULT ,
} , {
. reg = PMBUS_VIRT_VMON_OV_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_VOLTAGE_OV_WARNING ,
} , {
. reg = PMBUS_VIRT_VMON_OV_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_VOLTAGE_OV_FAULT ,
}
} ;
2011-03-11 18:09:33 -08:00
static const struct pmbus_limit_attr vout_limit_attrs [ ] = {
{
. reg = PMBUS_VOUT_UV_WARN_LIMIT ,
. attr = " min " ,
. alarm = " min_alarm " ,
. sbit = PB_VOLTAGE_UV_WARNING ,
} , {
. reg = PMBUS_VOUT_UV_FAULT_LIMIT ,
. attr = " lcrit " ,
. alarm = " lcrit_alarm " ,
. sbit = PB_VOLTAGE_UV_FAULT ,
} , {
. reg = PMBUS_VOUT_OV_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_VOLTAGE_OV_WARNING ,
} , {
. reg = PMBUS_VOUT_OV_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_VOLTAGE_OV_FAULT ,
2011-07-09 08:30:26 -07:00
} , {
. reg = PMBUS_VIRT_READ_VOUT_AVG ,
. update = true ,
. attr = " average " ,
} , {
. reg = PMBUS_VIRT_READ_VOUT_MIN ,
. update = true ,
. attr = " lowest " ,
} , {
. reg = PMBUS_VIRT_READ_VOUT_MAX ,
. update = true ,
. attr = " highest " ,
} , {
. reg = PMBUS_VIRT_RESET_VOUT_HISTORY ,
. attr = " reset_history " ,
2011-01-26 20:09:02 -08:00
}
2011-03-11 18:09:33 -08:00
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_sensor_attr voltage_attributes [ ] = {
{
. reg = PMBUS_READ_VIN ,
. class = PSC_VOLTAGE_IN ,
. label = " vin " ,
. func = PMBUS_HAVE_VIN ,
. sfunc = PMBUS_HAVE_STATUS_INPUT ,
. sbase = PB_STATUS_INPUT_BASE ,
. gbit = PB_STATUS_VIN_UV ,
. limit = vin_limit_attrs ,
. nlimit = ARRAY_SIZE ( vin_limit_attrs ) ,
2013-01-16 10:31:32 -08:00
} , {
. reg = PMBUS_VIRT_READ_VMON ,
. class = PSC_VOLTAGE_IN ,
. label = " vmon " ,
. func = PMBUS_HAVE_VMON ,
. sfunc = PMBUS_HAVE_STATUS_VMON ,
. sbase = PB_STATUS_VMON_BASE ,
. limit = vmon_limit_attrs ,
. nlimit = ARRAY_SIZE ( vmon_limit_attrs ) ,
2011-03-11 18:09:33 -08:00
} , {
. reg = PMBUS_READ_VCAP ,
. class = PSC_VOLTAGE_IN ,
. label = " vcap " ,
. func = PMBUS_HAVE_VCAP ,
} , {
. reg = PMBUS_READ_VOUT ,
. class = PSC_VOLTAGE_OUT ,
. label = " vout " ,
. paged = true ,
. func = PMBUS_HAVE_VOUT ,
. sfunc = PMBUS_HAVE_STATUS_VOUT ,
. sbase = PB_STATUS_VOUT_BASE ,
. gbit = PB_STATUS_VOUT_OV ,
. limit = vout_limit_attrs ,
. nlimit = ARRAY_SIZE ( vout_limit_attrs ) ,
2011-01-26 20:09:02 -08:00
}
2011-03-11 18:09:33 -08:00
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
/* Current attributes */
static const struct pmbus_limit_attr iin_limit_attrs [ ] = {
{
. reg = PMBUS_IIN_OC_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_IIN_OC_WARNING ,
} , {
. reg = PMBUS_IIN_OC_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_IIN_OC_FAULT ,
2011-07-09 08:30:26 -07:00
} , {
. reg = PMBUS_VIRT_READ_IIN_AVG ,
. update = true ,
. attr = " average " ,
} , {
. reg = PMBUS_VIRT_READ_IIN_MIN ,
. update = true ,
. attr = " lowest " ,
} , {
. reg = PMBUS_VIRT_READ_IIN_MAX ,
. update = true ,
. attr = " highest " ,
} , {
. reg = PMBUS_VIRT_RESET_IIN_HISTORY ,
. attr = " reset_history " ,
2011-03-11 18:09:33 -08:00
}
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_limit_attr iout_limit_attrs [ ] = {
{
. reg = PMBUS_IOUT_OC_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_IOUT_OC_WARNING ,
} , {
. reg = PMBUS_IOUT_UC_FAULT_LIMIT ,
. attr = " lcrit " ,
. alarm = " lcrit_alarm " ,
. sbit = PB_IOUT_UC_FAULT ,
} , {
. reg = PMBUS_IOUT_OC_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_IOUT_OC_FAULT ,
2011-07-09 08:30:26 -07:00
} , {
. reg = PMBUS_VIRT_READ_IOUT_AVG ,
. update = true ,
. attr = " average " ,
} , {
. reg = PMBUS_VIRT_READ_IOUT_MIN ,
. update = true ,
. attr = " lowest " ,
} , {
. reg = PMBUS_VIRT_READ_IOUT_MAX ,
. update = true ,
. attr = " highest " ,
} , {
. reg = PMBUS_VIRT_RESET_IOUT_HISTORY ,
. attr = " reset_history " ,
2011-03-11 18:09:33 -08:00
}
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_sensor_attr current_attributes [ ] = {
{
. reg = PMBUS_READ_IIN ,
. class = PSC_CURRENT_IN ,
. label = " iin " ,
. func = PMBUS_HAVE_IIN ,
. sfunc = PMBUS_HAVE_STATUS_INPUT ,
. sbase = PB_STATUS_INPUT_BASE ,
2017-08-10 16:57:49 -05:00
. gbit = PB_STATUS_INPUT ,
2011-03-11 18:09:33 -08:00
. limit = iin_limit_attrs ,
. nlimit = ARRAY_SIZE ( iin_limit_attrs ) ,
} , {
. reg = PMBUS_READ_IOUT ,
. class = PSC_CURRENT_OUT ,
. label = " iout " ,
. paged = true ,
. func = PMBUS_HAVE_IOUT ,
. sfunc = PMBUS_HAVE_STATUS_IOUT ,
. sbase = PB_STATUS_IOUT_BASE ,
. gbit = PB_STATUS_IOUT_OC ,
. limit = iout_limit_attrs ,
. nlimit = ARRAY_SIZE ( iout_limit_attrs ) ,
2011-01-26 20:09:02 -08:00
}
2011-03-11 18:09:33 -08:00
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
/* Power attributes */
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_limit_attr pin_limit_attrs [ ] = {
{
. reg = PMBUS_PIN_OP_WARN_LIMIT ,
. attr = " max " ,
. alarm = " alarm " ,
. sbit = PB_PIN_OP_WARNING ,
2011-07-09 08:30:26 -07:00
} , {
. reg = PMBUS_VIRT_READ_PIN_AVG ,
. update = true ,
. attr = " average " ,
2015-07-05 13:45:43 -07:00
} , {
. reg = PMBUS_VIRT_READ_PIN_MIN ,
. update = true ,
. attr = " input_lowest " ,
2011-07-09 08:30:26 -07:00
} , {
. reg = PMBUS_VIRT_READ_PIN_MAX ,
. update = true ,
. attr = " input_highest " ,
} , {
. reg = PMBUS_VIRT_RESET_PIN_HISTORY ,
. attr = " reset_history " ,
2011-03-11 18:09:33 -08:00
}
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_limit_attr pout_limit_attrs [ ] = {
{
. reg = PMBUS_POUT_MAX ,
. attr = " cap " ,
. alarm = " cap_alarm " ,
. sbit = PB_POWER_LIMITING ,
} , {
. reg = PMBUS_POUT_OP_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_POUT_OP_WARNING ,
} , {
. reg = PMBUS_POUT_OP_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_POUT_OP_FAULT ,
2012-02-23 19:33:55 -08:00
} , {
. reg = PMBUS_VIRT_READ_POUT_AVG ,
. update = true ,
. attr = " average " ,
2015-07-05 13:45:43 -07:00
} , {
. reg = PMBUS_VIRT_READ_POUT_MIN ,
. update = true ,
. attr = " input_lowest " ,
2012-02-23 19:33:55 -08:00
} , {
. reg = PMBUS_VIRT_READ_POUT_MAX ,
. update = true ,
. attr = " input_highest " ,
} , {
. reg = PMBUS_VIRT_RESET_POUT_HISTORY ,
. attr = " reset_history " ,
2011-03-11 18:09:33 -08:00
}
} ;
2011-03-06 10:56:52 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_sensor_attr power_attributes [ ] = {
{
. reg = PMBUS_READ_PIN ,
. class = PSC_POWER ,
. label = " pin " ,
. func = PMBUS_HAVE_PIN ,
. sfunc = PMBUS_HAVE_STATUS_INPUT ,
. sbase = PB_STATUS_INPUT_BASE ,
2017-08-10 16:57:49 -05:00
. gbit = PB_STATUS_INPUT ,
2011-03-11 18:09:33 -08:00
. limit = pin_limit_attrs ,
. nlimit = ARRAY_SIZE ( pin_limit_attrs ) ,
} , {
. reg = PMBUS_READ_POUT ,
. class = PSC_POWER ,
. label = " pout " ,
. paged = true ,
. func = PMBUS_HAVE_POUT ,
. sfunc = PMBUS_HAVE_STATUS_IOUT ,
. sbase = PB_STATUS_IOUT_BASE ,
. limit = pout_limit_attrs ,
. nlimit = ARRAY_SIZE ( pout_limit_attrs ) ,
}
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
/* Temperature atributes */
static const struct pmbus_limit_attr temp_limit_attrs [ ] = {
2011-07-09 08:30:26 -07:00
{
. reg = PMBUS_UT_WARN_LIMIT ,
2011-09-10 06:02:12 -07:00
. low = true ,
2011-07-09 08:30:26 -07:00
. attr = " min " ,
. alarm = " min_alarm " ,
. sbit = PB_TEMP_UT_WARNING ,
} , {
. reg = PMBUS_UT_FAULT_LIMIT ,
2011-09-10 06:02:12 -07:00
. low = true ,
2011-07-09 08:30:26 -07:00
. attr = " lcrit " ,
. alarm = " lcrit_alarm " ,
. sbit = PB_TEMP_UT_FAULT ,
} , {
. reg = PMBUS_OT_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_TEMP_OT_WARNING ,
} , {
. reg = PMBUS_OT_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_TEMP_OT_FAULT ,
} , {
. reg = PMBUS_VIRT_READ_TEMP_MIN ,
. attr = " lowest " ,
2012-02-23 19:33:55 -08:00
} , {
. reg = PMBUS_VIRT_READ_TEMP_AVG ,
. attr = " average " ,
2011-07-09 08:30:26 -07:00
} , {
. reg = PMBUS_VIRT_READ_TEMP_MAX ,
. attr = " highest " ,
} , {
. reg = PMBUS_VIRT_RESET_TEMP_HISTORY ,
. attr = " reset_history " ,
}
} ;
2011-09-10 12:59:15 -07:00
static const struct pmbus_limit_attr temp_limit_attrs2 [ ] = {
{
. reg = PMBUS_UT_WARN_LIMIT ,
. low = true ,
. attr = " min " ,
. alarm = " min_alarm " ,
. sbit = PB_TEMP_UT_WARNING ,
} , {
. reg = PMBUS_UT_FAULT_LIMIT ,
. low = true ,
. attr = " lcrit " ,
. alarm = " lcrit_alarm " ,
. sbit = PB_TEMP_UT_FAULT ,
} , {
. reg = PMBUS_OT_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_TEMP_OT_WARNING ,
} , {
. reg = PMBUS_OT_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_TEMP_OT_FAULT ,
} , {
. reg = PMBUS_VIRT_READ_TEMP2_MIN ,
. attr = " lowest " ,
2012-02-23 19:33:55 -08:00
} , {
. reg = PMBUS_VIRT_READ_TEMP2_AVG ,
. attr = " average " ,
2011-09-10 12:59:15 -07:00
} , {
. reg = PMBUS_VIRT_READ_TEMP2_MAX ,
. attr = " highest " ,
} , {
. reg = PMBUS_VIRT_RESET_TEMP2_HISTORY ,
. attr = " reset_history " ,
}
} ;
static const struct pmbus_limit_attr temp_limit_attrs3 [ ] = {
2011-03-11 18:09:33 -08:00
{
. reg = PMBUS_UT_WARN_LIMIT ,
2011-09-10 06:02:12 -07:00
. low = true ,
2011-03-11 18:09:33 -08:00
. attr = " min " ,
. alarm = " min_alarm " ,
. sbit = PB_TEMP_UT_WARNING ,
} , {
. reg = PMBUS_UT_FAULT_LIMIT ,
2011-09-10 06:02:12 -07:00
. low = true ,
2011-03-11 18:09:33 -08:00
. attr = " lcrit " ,
. alarm = " lcrit_alarm " ,
. sbit = PB_TEMP_UT_FAULT ,
} , {
. reg = PMBUS_OT_WARN_LIMIT ,
. attr = " max " ,
. alarm = " max_alarm " ,
. sbit = PB_TEMP_OT_WARNING ,
} , {
. reg = PMBUS_OT_FAULT_LIMIT ,
. attr = " crit " ,
. alarm = " crit_alarm " ,
. sbit = PB_TEMP_OT_FAULT ,
}
} ;
2011-01-26 20:09:02 -08:00
2011-03-11 18:09:33 -08:00
static const struct pmbus_sensor_attr temp_attributes [ ] = {
{
. reg = PMBUS_READ_TEMPERATURE_1 ,
. class = PSC_TEMPERATURE ,
. paged = true ,
. update = true ,
. compare = true ,
. func = PMBUS_HAVE_TEMP ,
. sfunc = PMBUS_HAVE_STATUS_TEMP ,
. sbase = PB_STATUS_TEMP_BASE ,
. gbit = PB_STATUS_TEMPERATURE ,
. limit = temp_limit_attrs ,
. nlimit = ARRAY_SIZE ( temp_limit_attrs ) ,
} , {
. reg = PMBUS_READ_TEMPERATURE_2 ,
. class = PSC_TEMPERATURE ,
. paged = true ,
. update = true ,
. compare = true ,
. func = PMBUS_HAVE_TEMP2 ,
. sfunc = PMBUS_HAVE_STATUS_TEMP ,
. sbase = PB_STATUS_TEMP_BASE ,
. gbit = PB_STATUS_TEMPERATURE ,
2011-09-10 12:59:15 -07:00
. limit = temp_limit_attrs2 ,
. nlimit = ARRAY_SIZE ( temp_limit_attrs2 ) ,
2011-03-11 18:09:33 -08:00
} , {
. reg = PMBUS_READ_TEMPERATURE_3 ,
. class = PSC_TEMPERATURE ,
. paged = true ,
. update = true ,
. compare = true ,
. func = PMBUS_HAVE_TEMP3 ,
. sfunc = PMBUS_HAVE_STATUS_TEMP ,
. sbase = PB_STATUS_TEMP_BASE ,
. gbit = PB_STATUS_TEMPERATURE ,
2011-09-10 12:59:15 -07:00
. limit = temp_limit_attrs3 ,
. nlimit = ARRAY_SIZE ( temp_limit_attrs3 ) ,
2011-01-26 20:09:02 -08:00
}
2011-03-11 18:09:33 -08:00
} ;
static const int pmbus_fan_registers [ ] = {
PMBUS_READ_FAN_SPEED_1 ,
PMBUS_READ_FAN_SPEED_2 ,
PMBUS_READ_FAN_SPEED_3 ,
PMBUS_READ_FAN_SPEED_4
} ;
static const int pmbus_fan_config_registers [ ] = {
PMBUS_FAN_CONFIG_12 ,
PMBUS_FAN_CONFIG_12 ,
PMBUS_FAN_CONFIG_34 ,
PMBUS_FAN_CONFIG_34
} ;
static const int pmbus_fan_status_registers [ ] = {
PMBUS_STATUS_FAN_12 ,
PMBUS_STATUS_FAN_12 ,
PMBUS_STATUS_FAN_34 ,
PMBUS_STATUS_FAN_34
} ;
static const u32 pmbus_fan_flags [ ] = {
PMBUS_HAVE_FAN12 ,
PMBUS_HAVE_FAN12 ,
PMBUS_HAVE_FAN34 ,
PMBUS_HAVE_FAN34
} ;
static const u32 pmbus_fan_status_flags [ ] = {
PMBUS_HAVE_STATUS_FAN12 ,
PMBUS_HAVE_STATUS_FAN12 ,
PMBUS_HAVE_STATUS_FAN34 ,
PMBUS_HAVE_STATUS_FAN34
} ;
/* Fans */
2013-01-20 08:13:21 -08:00
static int pmbus_add_fan_attributes ( struct i2c_client * client ,
struct pmbus_data * data )
2011-03-11 18:09:33 -08:00
{
const struct pmbus_driver_info * info = data - > info ;
int index = 1 ;
int page ;
2013-01-20 08:13:21 -08:00
int ret ;
2011-01-26 20:09:02 -08:00
for ( page = 0 ; page < info - > pages ; page + + ) {
2011-03-05 07:55:10 -08:00
int f ;
2011-01-26 20:09:02 -08:00
2011-03-05 07:55:10 -08:00
for ( f = 0 ; f < ARRAY_SIZE ( pmbus_fan_registers ) ; f + + ) {
2011-01-26 20:09:02 -08:00
int regval ;
2011-03-05 07:55:10 -08:00
if ( ! ( info - > func [ page ] & pmbus_fan_flags [ f ] ) )
break ;
2011-01-26 20:09:02 -08:00
if ( ! pmbus_check_word_register ( client , page ,
2011-03-09 07:23:54 -08:00
pmbus_fan_registers [ f ] ) )
2011-01-26 20:09:02 -08:00
break ;
/*
* Skip fan if not installed .
* Each fan configuration register covers multiple fans ,
* so we have to do some magic .
*/
2011-03-09 07:23:54 -08:00
regval = _pmbus_read_byte_data ( client , page ,
2011-01-26 20:09:02 -08:00
pmbus_fan_config_registers [ f ] ) ;
if ( regval < 0 | |
( ! ( regval & ( PB_FAN_1_INSTALLED > > ( ( f & 1 ) * 4 ) ) ) ) )
continue ;
2013-01-20 12:01:41 -08:00
if ( pmbus_add_sensor ( data , " fan " , " input " , index ,
page , pmbus_fan_registers [ f ] ,
PSC_FAN , true , true ) = = NULL )
return - ENOMEM ;
2011-01-26 20:09:02 -08:00
/*
* Each fan status register covers multiple fans ,
* so we have to do some magic .
*/
2011-03-05 07:55:10 -08:00
if ( ( info - > func [ page ] & pmbus_fan_status_flags [ f ] ) & &
pmbus_check_byte_register ( client ,
page , pmbus_fan_status_registers [ f ] ) ) {
2011-01-26 20:09:02 -08:00
int base ;
if ( f > 1 ) /* fan 3, 4 */
2011-03-05 07:55:10 -08:00
base = PB_STATUS_FAN34_BASE + page ;
2011-01-26 20:09:02 -08:00
else
base = PB_STATUS_FAN_BASE + page ;
2013-01-20 10:05:55 -08:00
ret = pmbus_add_boolean ( data , " fan " ,
" alarm " , index , NULL , NULL , base ,
2011-01-26 20:09:02 -08:00
PB_FAN_FAN1_WARNING > > ( f & 1 ) ) ;
2013-01-20 08:13:21 -08:00
if ( ret )
return ret ;
2013-01-20 10:05:55 -08:00
ret = pmbus_add_boolean ( data , " fan " ,
" fault " , index , NULL , NULL , base ,
2011-01-26 20:09:02 -08:00
PB_FAN_FAN1_FAULT > > ( f & 1 ) ) ;
2013-01-20 08:13:21 -08:00
if ( ret )
return ret ;
2011-01-26 20:09:02 -08:00
}
2011-03-11 18:09:33 -08:00
index + + ;
2011-01-26 20:09:02 -08:00
}
}
2013-01-20 08:13:21 -08:00
return 0 ;
2011-01-26 20:09:02 -08:00
}
2013-01-20 08:13:21 -08:00
static int pmbus_find_attributes ( struct i2c_client * client ,
struct pmbus_data * data )
2011-03-11 18:09:33 -08:00
{
2013-01-20 08:13:21 -08:00
int ret ;
2011-03-11 18:09:33 -08:00
/* Voltage sensors */
2013-01-20 08:13:21 -08:00
ret = pmbus_add_sensor_attrs ( client , data , " in " , voltage_attributes ,
ARRAY_SIZE ( voltage_attributes ) ) ;
if ( ret )
return ret ;
2011-03-11 18:09:33 -08:00
/* Current sensors */
2013-01-20 08:13:21 -08:00
ret = pmbus_add_sensor_attrs ( client , data , " curr " , current_attributes ,
ARRAY_SIZE ( current_attributes ) ) ;
if ( ret )
return ret ;
2011-03-11 18:09:33 -08:00
/* Power sensors */
2013-01-20 08:13:21 -08:00
ret = pmbus_add_sensor_attrs ( client , data , " power " , power_attributes ,
ARRAY_SIZE ( power_attributes ) ) ;
if ( ret )
return ret ;
2011-03-11 18:09:33 -08:00
/* Temperature sensors */
2013-01-20 08:13:21 -08:00
ret = pmbus_add_sensor_attrs ( client , data , " temp " , temp_attributes ,
ARRAY_SIZE ( temp_attributes ) ) ;
if ( ret )
return ret ;
2011-03-11 18:09:33 -08:00
/* Fans */
2013-01-20 08:13:21 -08:00
ret = pmbus_add_fan_attributes ( client , data ) ;
return ret ;
2011-03-11 18:09:33 -08:00
}
2011-01-26 20:09:02 -08:00
/*
* Identify chip parameters .
* This function is called for all chips .
*/
static int pmbus_identify_common ( struct i2c_client * client ,
2014-01-30 19:51:14 -08:00
struct pmbus_data * data , int page )
2011-01-26 20:09:02 -08:00
{
2011-09-28 11:36:20 -07:00
int vout_mode = - 1 ;
2011-01-26 20:09:02 -08:00
2014-01-30 19:51:14 -08:00
if ( pmbus_check_byte_register ( client , page , PMBUS_VOUT_MODE ) )
vout_mode = _pmbus_read_byte_data ( client , page ,
PMBUS_VOUT_MODE ) ;
2011-03-01 13:49:18 -08:00
if ( vout_mode > = 0 & & vout_mode ! = 0xff ) {
2011-01-26 20:09:02 -08:00
/*
* Not all chips support the VOUT_MODE command ,
* so a failure to read it is not an error .
*/
switch ( vout_mode > > 5 ) {
case 0 : /* linear mode */
2011-06-25 11:21:49 -07:00
if ( data - > info - > format [ PSC_VOLTAGE_OUT ] ! = linear )
2011-01-26 20:09:02 -08:00
return - ENODEV ;
2014-01-30 19:51:14 -08:00
data - > exponent [ page ] = ( ( s8 ) ( vout_mode < < 3 ) ) > > 3 ;
2011-01-26 20:09:02 -08:00
break ;
2011-06-25 11:21:49 -07:00
case 1 : /* VID mode */
if ( data - > info - > format [ PSC_VOLTAGE_OUT ] ! = vid )
return - ENODEV ;
break ;
2011-01-26 20:09:02 -08:00
case 2 : /* direct mode */
2011-06-25 11:21:49 -07:00
if ( data - > info - > format [ PSC_VOLTAGE_OUT ] ! = direct )
2011-01-26 20:09:02 -08:00
return - ENODEV ;
break ;
default :
return - ENODEV ;
}
}
2014-01-30 19:51:14 -08:00
pmbus_clear_fault_page ( client , page ) ;
2011-01-26 20:09:02 -08:00
return 0 ;
}
2017-08-10 16:57:48 -05:00
static int pmbus_read_status_byte ( struct i2c_client * client , int page )
{
return _pmbus_read_byte_data ( client , page , PMBUS_STATUS_BYTE ) ;
}
static int pmbus_read_status_word ( struct i2c_client * client , int page )
{
return _pmbus_read_word_data ( client , page , PMBUS_STATUS_WORD ) ;
}
2012-05-19 11:35:25 -07:00
static int pmbus_init_common ( struct i2c_client * client , struct pmbus_data * data ,
struct pmbus_driver_info * info )
{
struct device * dev = & client - > dev ;
2014-01-30 19:51:14 -08:00
int page , ret ;
2012-05-19 11:35:25 -07:00
/*
2017-08-10 16:57:48 -05:00
* Some PMBus chips don ' t support PMBUS_STATUS_WORD , so try
* to use PMBUS_STATUS_BYTE instead if that is the case .
2012-05-19 11:35:25 -07:00
* Bail out if both registers are not supported .
*/
2017-08-10 16:57:48 -05:00
data - > read_status = pmbus_read_status_word ;
ret = i2c_smbus_read_word_data ( client , PMBUS_STATUS_WORD ) ;
if ( ret < 0 | | ret = = 0xffff ) {
data - > read_status = pmbus_read_status_byte ;
ret = i2c_smbus_read_byte_data ( client , PMBUS_STATUS_BYTE ) ;
if ( ret < 0 | | ret = = 0xff ) {
2012-05-19 11:35:25 -07:00
dev_err ( dev , " PMBus status register not found \n " ) ;
return - ENODEV ;
}
2017-08-10 16:57:49 -05:00
} else {
data - > has_status_word = true ;
2012-05-19 11:35:25 -07:00
}
2015-08-17 16:26:21 -07:00
/* Enable PEC if the controller supports it */
ret = i2c_smbus_read_byte_data ( client , PMBUS_CAPABILITY ) ;
if ( ret > = 0 & & ( ret & PB_CAPABILITY_ERROR_CHECK ) )
client - > flags | = I2C_CLIENT_PEC ;
2012-05-19 11:35:25 -07:00
pmbus_clear_faults ( client ) ;
if ( info - > identify ) {
ret = ( * info - > identify ) ( client , info ) ;
if ( ret < 0 ) {
dev_err ( dev , " Chip identification failed \n " ) ;
return ret ;
}
}
if ( info - > pages < = 0 | | info - > pages > PMBUS_PAGES ) {
dev_err ( dev , " Bad number of PMBus pages: %d \n " , info - > pages ) ;
return - ENODEV ;
}
2014-01-30 19:51:14 -08:00
for ( page = 0 ; page < info - > pages ; page + + ) {
ret = pmbus_identify_common ( client , data , page ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to identify chip capabilities \n " ) ;
return ret ;
}
2012-05-19 11:35:25 -07:00
}
return 0 ;
}
2014-10-15 13:55:09 -05:00
# if IS_ENABLED(CONFIG_REGULATOR)
static int pmbus_regulator_is_enabled ( struct regulator_dev * rdev )
{
struct device * dev = rdev_get_dev ( rdev ) ;
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
u8 page = rdev_get_id ( rdev ) ;
int ret ;
ret = pmbus_read_byte_data ( client , page , PMBUS_OPERATION ) ;
if ( ret < 0 )
return ret ;
return ! ! ( ret & PB_OPERATION_CONTROL_ON ) ;
}
static int _pmbus_regulator_on_off ( struct regulator_dev * rdev , bool enable )
{
struct device * dev = rdev_get_dev ( rdev ) ;
struct i2c_client * client = to_i2c_client ( dev - > parent ) ;
u8 page = rdev_get_id ( rdev ) ;
return pmbus_update_byte_data ( client , page , PMBUS_OPERATION ,
PB_OPERATION_CONTROL_ON ,
enable ? PB_OPERATION_CONTROL_ON : 0 ) ;
}
static int pmbus_regulator_enable ( struct regulator_dev * rdev )
{
return _pmbus_regulator_on_off ( rdev , 1 ) ;
}
static int pmbus_regulator_disable ( struct regulator_dev * rdev )
{
return _pmbus_regulator_on_off ( rdev , 0 ) ;
}
2015-07-10 13:00:08 +08:00
const struct regulator_ops pmbus_regulator_ops = {
2014-10-15 13:55:09 -05:00
. enable = pmbus_regulator_enable ,
. disable = pmbus_regulator_disable ,
. is_enabled = pmbus_regulator_is_enabled ,
} ;
EXPORT_SYMBOL_GPL ( pmbus_regulator_ops ) ;
static int pmbus_regulator_register ( struct pmbus_data * data )
{
struct device * dev = data - > dev ;
const struct pmbus_driver_info * info = data - > info ;
const struct pmbus_platform_data * pdata = dev_get_platdata ( dev ) ;
struct regulator_dev * rdev ;
int i ;
for ( i = 0 ; i < info - > num_regulators ; i + + ) {
struct regulator_config config = { } ;
config . dev = dev ;
config . driver_data = data ;
if ( pdata & & pdata - > reg_init_data )
config . init_data = & pdata - > reg_init_data [ i ] ;
rdev = devm_regulator_register ( dev , & info - > reg_desc [ i ] ,
& config ) ;
if ( IS_ERR ( rdev ) ) {
dev_err ( dev , " Failed to register %s regulator \n " ,
info - > reg_desc [ i ] . name ) ;
return PTR_ERR ( rdev ) ;
}
}
return 0 ;
}
# else
static int pmbus_regulator_register ( struct pmbus_data * data )
{
return 0 ;
}
# endif
2011-01-26 20:09:02 -08:00
int pmbus_do_probe ( struct i2c_client * client , const struct i2c_device_id * id ,
struct pmbus_driver_info * info )
{
2013-01-20 08:23:16 -08:00
struct device * dev = & client - > dev ;
2013-07-30 17:13:06 +09:00
const struct pmbus_platform_data * pdata = dev_get_platdata ( dev ) ;
2011-01-26 20:09:02 -08:00
struct pmbus_data * data ;
int ret ;
2013-01-16 10:20:15 -08:00
if ( ! info )
2011-01-26 20:09:02 -08:00
return - ENODEV ;
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_SMBUS_WRITE_BYTE
| I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA ) )
return - ENODEV ;
2013-01-20 08:23:16 -08:00
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
2013-01-16 10:20:15 -08:00
if ( ! data )
2011-01-26 20:09:02 -08:00
return - ENOMEM ;
i2c_set_clientdata ( client , data ) ;
mutex_init ( & data - > update_lock ) ;
2013-01-20 08:13:21 -08:00
data - > dev = dev ;
2011-01-26 20:09:02 -08:00
if ( pdata )
data - > flags = pdata - > flags ;
data - > info = info ;
2012-05-19 11:35:25 -07:00
ret = pmbus_init_common ( client , data , info ) ;
if ( ret < 0 )
2012-02-22 08:56:43 -08:00
return ret ;
2011-01-26 20:09:02 -08:00
2013-01-20 08:13:21 -08:00
ret = pmbus_find_attributes ( client , data ) ;
if ( ret )
2013-01-20 21:00:01 -08:00
goto out_kfree ;
2011-01-26 20:09:02 -08:00
/*
* If there are no attributes , something is wrong .
* Bail out instead of trying to register nothing .
*/
if ( ! data - > num_attributes ) {
2013-01-20 08:23:16 -08:00
dev_err ( dev , " No attributes found \n " ) ;
2013-01-20 21:00:01 -08:00
ret = - ENODEV ;
goto out_kfree ;
2011-01-26 20:09:02 -08:00
}
2013-07-06 09:47:08 -07:00
data - > groups [ 0 ] = & data - > group ;
data - > hwmon_dev = hwmon_device_register_with_groups ( dev , client - > name ,
data , data - > groups ) ;
2011-01-26 20:09:02 -08:00
if ( IS_ERR ( data - > hwmon_dev ) ) {
ret = PTR_ERR ( data - > hwmon_dev ) ;
2013-01-20 08:23:16 -08:00
dev_err ( dev , " Failed to register hwmon device \n " ) ;
2013-07-06 09:47:08 -07:00
goto out_kfree ;
2011-01-26 20:09:02 -08:00
}
2014-10-15 13:55:09 -05:00
ret = pmbus_regulator_register ( data ) ;
if ( ret )
goto out_unregister ;
2011-01-26 20:09:02 -08:00
return 0 ;
2014-10-15 13:55:09 -05:00
out_unregister :
hwmon_device_unregister ( data - > hwmon_dev ) ;
2013-01-20 21:00:01 -08:00
out_kfree :
kfree ( data - > group . attrs ) ;
2011-01-26 20:09:02 -08:00
return ret ;
}
EXPORT_SYMBOL_GPL ( pmbus_do_probe ) ;
2012-02-22 08:56:44 -08:00
int pmbus_do_remove ( struct i2c_client * client )
2011-01-26 20:09:02 -08:00
{
struct pmbus_data * data = i2c_get_clientdata ( client ) ;
hwmon_device_unregister ( data - > hwmon_dev ) ;
2013-01-20 21:00:01 -08:00
kfree ( data - > group . attrs ) ;
2012-02-22 08:56:44 -08:00
return 0 ;
2011-01-26 20:09:02 -08:00
}
EXPORT_SYMBOL_GPL ( pmbus_do_remove ) ;
MODULE_AUTHOR ( " Guenter Roeck " ) ;
MODULE_DESCRIPTION ( " PMBus core driver " ) ;
MODULE_LICENSE ( " GPL " ) ;