2019-12-14 06:37:31 -08:00
// SPDX-License-Identifier: GPL-2.0
/*
2021-01-25 10:53:27 -08:00
* Hardware monitoring driver for Maxim MAX16508 and MAX16601 .
2019-12-14 06:37:31 -08:00
*
* Implementation notes :
*
2021-01-25 10:53:27 -08:00
* This chip series supports two rails , VCORE and VSA . Telemetry information
* for the two rails is reported in two subsequent I2C addresses . The driver
2019-12-14 06:37:31 -08:00
* instantiates a dummy I2C client at the second I2C address to report
* information for the VSA rail in a single instance of the driver .
* Telemetry for the VSA rail is reported to the PMBus core in PMBus page 2.
*
* The chip reports input current using two separate methods . The input current
* reported with the standard READ_IIN command is derived from the output
* current . The first method is reported to the PMBus core with PMBus page 0 ,
* the second method is reported with PMBus page 1.
*
* The chip supports reading per - phase temperatures and per - phase input / output
* currents for VCORE . Telemetry is reported in vendor specific registers .
* The driver translates the vendor specific register values to PMBus standard
* register values and reports per - phase information in PMBus page 0.
*
* Copyright 2019 , 2020 Google LLC .
*/
# include <linux/bits.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include "pmbus.h"
2021-01-25 10:53:27 -08:00
enum chips { max16508 , max16601 } ;
2021-01-25 10:53:26 -08:00
# define REG_DEFAULT_NUM_POP 0xc4
2019-12-14 06:37:31 -08:00
# define REG_SETPT_DVID 0xd1
# define DAC_10MV_MODE BIT(4)
# define REG_IOUT_AVG_PK 0xee
# define REG_IIN_SENSOR 0xf1
# define REG_TOTAL_INPUT_POWER 0xf2
# define REG_PHASE_ID 0xf3
# define CORE_RAIL_INDICATOR BIT(7)
# define REG_PHASE_REPORTING 0xf4
2021-01-25 10:53:26 -08:00
# define MAX16601_NUM_PHASES 8
2019-12-14 06:37:31 -08:00
struct max16601_data {
2021-01-25 10:53:27 -08:00
enum chips id ;
2019-12-14 06:37:31 -08:00
struct pmbus_driver_info info ;
struct i2c_client * vsa ;
int iout_avg_pkg ;
} ;
# define to_max16601_data(x) container_of(x, struct max16601_data, info)
static int max16601_read_byte ( struct i2c_client * client , int page , int reg )
{
const struct pmbus_driver_info * info = pmbus_get_driver_info ( client ) ;
struct max16601_data * data = to_max16601_data ( info ) ;
if ( page > 0 ) {
if ( page = = 2 ) /* VSA */
return i2c_smbus_read_byte_data ( data - > vsa , reg ) ;
return - EOPNOTSUPP ;
}
return - ENODATA ;
}
static int max16601_read_word ( struct i2c_client * client , int page , int phase ,
int reg )
{
const struct pmbus_driver_info * info = pmbus_get_driver_info ( client ) ;
struct max16601_data * data = to_max16601_data ( info ) ;
u8 buf [ I2C_SMBUS_BLOCK_MAX + 1 ] ;
int ret ;
switch ( page ) {
case 0 : /* VCORE */
if ( phase = = 0xff )
return - ENODATA ;
switch ( reg ) {
case PMBUS_READ_IIN :
case PMBUS_READ_IOUT :
case PMBUS_READ_TEMPERATURE_1 :
ret = i2c_smbus_write_byte_data ( client , REG_PHASE_ID ,
phase ) ;
if ( ret )
return ret ;
ret = i2c_smbus_read_block_data ( client ,
REG_PHASE_REPORTING ,
buf ) ;
if ( ret < 0 )
return ret ;
if ( ret < 6 )
return - EIO ;
switch ( reg ) {
case PMBUS_READ_TEMPERATURE_1 :
return buf [ 1 ] < < 8 | buf [ 0 ] ;
case PMBUS_READ_IOUT :
return buf [ 3 ] < < 8 | buf [ 2 ] ;
case PMBUS_READ_IIN :
return buf [ 5 ] < < 8 | buf [ 4 ] ;
default :
break ;
}
}
return - EOPNOTSUPP ;
case 1 : /* VCORE, read IIN/PIN from sensor element */
switch ( reg ) {
case PMBUS_READ_IIN :
return i2c_smbus_read_word_data ( client , REG_IIN_SENSOR ) ;
case PMBUS_READ_PIN :
return i2c_smbus_read_word_data ( client ,
REG_TOTAL_INPUT_POWER ) ;
default :
break ;
}
return - EOPNOTSUPP ;
case 2 : /* VSA */
switch ( reg ) {
case PMBUS_VIRT_READ_IOUT_MAX :
ret = i2c_smbus_read_word_data ( data - > vsa ,
REG_IOUT_AVG_PK ) ;
if ( ret < 0 )
return ret ;
if ( sign_extend32 ( ret , 10 ) >
sign_extend32 ( data - > iout_avg_pkg , 10 ) )
data - > iout_avg_pkg = ret ;
return data - > iout_avg_pkg ;
case PMBUS_VIRT_RESET_IOUT_HISTORY :
return 0 ;
case PMBUS_IOUT_OC_FAULT_LIMIT :
case PMBUS_IOUT_OC_WARN_LIMIT :
case PMBUS_OT_FAULT_LIMIT :
case PMBUS_OT_WARN_LIMIT :
case PMBUS_READ_IIN :
case PMBUS_READ_IOUT :
case PMBUS_READ_TEMPERATURE_1 :
case PMBUS_STATUS_WORD :
return i2c_smbus_read_word_data ( data - > vsa , reg ) ;
default :
return - EOPNOTSUPP ;
}
default :
return - EOPNOTSUPP ;
}
}
static int max16601_write_byte ( struct i2c_client * client , int page , u8 reg )
{
const struct pmbus_driver_info * info = pmbus_get_driver_info ( client ) ;
struct max16601_data * data = to_max16601_data ( info ) ;
if ( page = = 2 ) {
if ( reg = = PMBUS_CLEAR_FAULTS )
return i2c_smbus_write_byte ( data - > vsa , reg ) ;
return - EOPNOTSUPP ;
}
return - ENODATA ;
}
static int max16601_write_word ( struct i2c_client * client , int page , int reg ,
u16 value )
{
const struct pmbus_driver_info * info = pmbus_get_driver_info ( client ) ;
struct max16601_data * data = to_max16601_data ( info ) ;
switch ( page ) {
case 0 : /* VCORE */
return - ENODATA ;
case 1 : /* VCORE IIN/PIN from sensor element */
default :
return - EOPNOTSUPP ;
case 2 : /* VSA */
switch ( reg ) {
case PMBUS_VIRT_RESET_IOUT_HISTORY :
data - > iout_avg_pkg = 0xfc00 ;
return 0 ;
case PMBUS_IOUT_OC_FAULT_LIMIT :
case PMBUS_IOUT_OC_WARN_LIMIT :
case PMBUS_OT_FAULT_LIMIT :
case PMBUS_OT_WARN_LIMIT :
return i2c_smbus_write_word_data ( data - > vsa , reg , value ) ;
default :
return - EOPNOTSUPP ;
}
}
}
static int max16601_identify ( struct i2c_client * client ,
struct pmbus_driver_info * info )
{
2021-01-25 10:53:27 -08:00
struct max16601_data * data = to_max16601_data ( info ) ;
2019-12-14 06:37:31 -08:00
int reg ;
reg = i2c_smbus_read_byte_data ( client , REG_SETPT_DVID ) ;
if ( reg < 0 )
return reg ;
if ( reg & DAC_10MV_MODE )
info - > vrm_version [ 0 ] = vr13 ;
else
info - > vrm_version [ 0 ] = vr12 ;
2021-01-25 10:53:27 -08:00
if ( data - > id ! = max16601 )
return 0 ;
2021-01-25 10:53:26 -08:00
reg = i2c_smbus_read_byte_data ( client , REG_DEFAULT_NUM_POP ) ;
if ( reg < 0 )
return reg ;
/*
* If REG_DEFAULT_NUM_POP returns 0 , we don ' t know how many phases
* are populated . Stick with the default in that case .
*/
reg & = 0x0f ;
if ( reg & & reg < = MAX16601_NUM_PHASES )
info - > phases [ 0 ] = reg ;
2019-12-14 06:37:31 -08:00
return 0 ;
}
static struct pmbus_driver_info max16601_info = {
. pages = 3 ,
. format [ PSC_VOLTAGE_IN ] = linear ,
. format [ PSC_VOLTAGE_OUT ] = vid ,
. format [ PSC_CURRENT_IN ] = linear ,
. format [ PSC_CURRENT_OUT ] = linear ,
. format [ PSC_TEMPERATURE ] = linear ,
. format [ PSC_POWER ] = linear ,
. func [ 0 ] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN |
PMBUS_HAVE_STATUS_INPUT |
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP |
PMBUS_HAVE_POUT | PMBUS_PAGE_VIRTUAL | PMBUS_PHASE_VIRTUAL ,
. func [ 1 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | PMBUS_PAGE_VIRTUAL ,
. func [ 2 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_PAGE_VIRTUAL ,
2021-01-25 10:53:26 -08:00
. phases [ 0 ] = MAX16601_NUM_PHASES ,
2019-12-14 06:37:31 -08:00
. pfunc [ 0 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP ,
. pfunc [ 1 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT ,
. pfunc [ 2 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP ,
. pfunc [ 3 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT ,
. pfunc [ 4 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP ,
. pfunc [ 5 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT ,
. pfunc [ 6 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP ,
. pfunc [ 7 ] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT ,
. identify = max16601_identify ,
. read_byte_data = max16601_read_byte ,
. read_word_data = max16601_read_word ,
. write_byte = max16601_write_byte ,
. write_word_data = max16601_write_word ,
} ;
static void max16601_remove ( void * _data )
{
struct max16601_data * data = _data ;
i2c_unregister_device ( data - > vsa ) ;
}
2021-01-25 10:53:27 -08:00
static const struct i2c_device_id max16601_id [ ] = {
{ " max16508 " , max16508 } ,
{ " max16601 " , max16601 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , max16601_id ) ;
static int max16601_get_id ( struct i2c_client * client )
2019-12-14 06:37:31 -08:00
{
struct device * dev = & client - > dev ;
u8 buf [ I2C_SMBUS_BLOCK_MAX + 1 ] ;
2021-01-25 10:53:27 -08:00
enum chips id ;
2019-12-14 06:37:31 -08:00
int ret ;
ret = i2c_smbus_read_block_data ( client , PMBUS_IC_DEVICE_ID , buf ) ;
2021-01-25 10:53:27 -08:00
if ( ret < 0 | | ret < 11 )
2019-12-14 06:37:31 -08:00
return - ENODEV ;
2021-01-25 10:53:27 -08:00
/*
* PMBUS_IC_DEVICE_ID is expected to return " MAX16601y.xx "
* or " MAX16500y.xx " .
*/
if ( ! strncmp ( buf , " MAX16500 " , 8 ) ) {
id = max16508 ;
} else if ( ! strncmp ( buf , " MAX16601 " , 8 ) ) {
id = max16601 ;
} else {
2019-12-14 06:37:31 -08:00
buf [ ret ] = ' \0 ' ;
dev_err ( dev , " Unsupported chip '%s' \n " , buf ) ;
return - ENODEV ;
}
2021-01-25 10:53:27 -08:00
return id ;
}
static int max16601_probe ( struct i2c_client * client )
{
struct device * dev = & client - > dev ;
const struct i2c_device_id * id ;
struct max16601_data * data ;
int ret , chip_id ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_BYTE_DATA |
I2C_FUNC_SMBUS_READ_BLOCK_DATA ) )
return - ENODEV ;
chip_id = max16601_get_id ( client ) ;
if ( chip_id < 0 )
return chip_id ;
id = i2c_match_id ( max16601_id , client ) ;
if ( chip_id ! = id - > driver_data )
dev_warn ( & client - > dev ,
" Device mismatch: Configured %s (%d), detected %d \n " ,
id - > name , ( int ) id - > driver_data , chip_id ) ;
2019-12-14 06:37:31 -08:00
ret = i2c_smbus_read_byte_data ( client , REG_PHASE_ID ) ;
if ( ret < 0 )
return ret ;
if ( ! ( ret & CORE_RAIL_INDICATOR ) ) {
dev_err ( dev ,
" Driver must be instantiated on CORE rail I2C address \n " ) ;
return - ENODEV ;
}
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2021-01-25 10:53:27 -08:00
data - > id = chip_id ;
2019-12-14 06:37:31 -08:00
data - > iout_avg_pkg = 0xfc00 ;
data - > vsa = i2c_new_dummy_device ( client - > adapter , client - > addr + 1 ) ;
if ( IS_ERR ( data - > vsa ) ) {
dev_err ( dev , " Failed to register VSA client \n " ) ;
return PTR_ERR ( data - > vsa ) ;
}
ret = devm_add_action_or_reset ( dev , max16601_remove , data ) ;
if ( ret )
return ret ;
data - > info = max16601_info ;
2020-08-08 23:00:04 +02:00
return pmbus_do_probe ( client , & data - > info ) ;
2019-12-14 06:37:31 -08:00
}
static struct i2c_driver max16601_driver = {
. driver = {
. name = " max16601 " ,
} ,
2020-08-08 23:00:04 +02:00
. probe_new = max16601_probe ,
2019-12-14 06:37:31 -08:00
. id_table = max16601_id ,
} ;
module_i2c_driver ( max16601_driver ) ;
MODULE_AUTHOR ( " Guenter Roeck <linux@roeck-us.net> " ) ;
MODULE_DESCRIPTION ( " PMBus driver for Maxim MAX16601 " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
2021-04-19 23:07:07 -07:00
MODULE_IMPORT_NS ( PMBUS ) ;