2021-06-09 11:32:08 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware monitoring driver for PIM4006 , PIM4328 and PIM4820
*
* Copyright ( c ) 2021 Flextronics International Sweden AB
*/
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pmbus.h>
# include <linux/slab.h>
# include "pmbus.h"
enum chips { pim4006 , pim4328 , pim4820 } ;
struct pim4328_data {
enum chips id ;
struct pmbus_driver_info info ;
} ;
# define to_pim4328_data(x) container_of(x, struct pim4328_data, info)
/* PIM4006 and PIM4328 */
# define PIM4328_MFR_READ_VINA 0xd3
# define PIM4328_MFR_READ_VINB 0xd4
/* PIM4006 */
# define PIM4328_MFR_READ_IINA 0xd6
# define PIM4328_MFR_READ_IINB 0xd7
# define PIM4328_MFR_FET_CHECKSTATUS 0xd9
/* PIM4328 */
# define PIM4328_MFR_STATUS_BITS 0xd5
/* PIM4820 */
# define PIM4328_MFR_READ_STATUS 0xd0
static const struct i2c_device_id pim4328_id [ ] = {
{ " bmr455 " , pim4328 } ,
{ " pim4006 " , pim4006 } ,
{ " pim4106 " , pim4006 } ,
{ " pim4206 " , pim4006 } ,
{ " pim4306 " , pim4006 } ,
{ " pim4328 " , pim4328 } ,
{ " pim4406 " , pim4006 } ,
{ " pim4820 " , pim4820 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , pim4328_id ) ;
static int pim4328_read_word_data ( struct i2c_client * client , int page ,
int phase , int reg )
{
int ret ;
if ( page > 0 )
return - ENXIO ;
if ( phase = = 0xff )
return - ENODATA ;
switch ( reg ) {
case PMBUS_READ_VIN :
ret = pmbus_read_word_data ( client , page , phase ,
phase = = 0 ? PIM4328_MFR_READ_VINA
: PIM4328_MFR_READ_VINB ) ;
break ;
case PMBUS_READ_IIN :
ret = pmbus_read_word_data ( client , page , phase ,
phase = = 0 ? PIM4328_MFR_READ_IINA
: PIM4328_MFR_READ_IINB ) ;
break ;
default :
ret = - ENODATA ;
}
return ret ;
}
static int pim4328_read_byte_data ( struct i2c_client * client , int page , int reg )
{
const struct pmbus_driver_info * info = pmbus_get_driver_info ( client ) ;
struct pim4328_data * data = to_pim4328_data ( info ) ;
int ret , status ;
if ( page > 0 )
return - ENXIO ;
switch ( reg ) {
case PMBUS_STATUS_BYTE :
ret = pmbus_read_byte_data ( client , page , PMBUS_STATUS_BYTE ) ;
if ( ret < 0 )
return ret ;
if ( data - > id = = pim4006 ) {
status = pmbus_read_word_data ( client , page , 0xff ,
PIM4328_MFR_FET_CHECKSTATUS ) ;
if ( status < 0 )
return status ;
if ( status & 0x0630 ) /* Input UV */
ret | = PB_STATUS_VIN_UV ;
} else if ( data - > id = = pim4328 ) {
status = pmbus_read_byte_data ( client , page ,
PIM4328_MFR_STATUS_BITS ) ;
if ( status < 0 )
return status ;
if ( status & 0x04 ) /* Input UV */
ret | = PB_STATUS_VIN_UV ;
if ( status & 0x40 ) /* Output UV */
ret | = PB_STATUS_NONE_ABOVE ;
} else if ( data - > id = = pim4820 ) {
status = pmbus_read_byte_data ( client , page ,
PIM4328_MFR_READ_STATUS ) ;
if ( status < 0 )
return status ;
if ( status & 0x05 ) /* Input OV or OC */
ret | = PB_STATUS_NONE_ABOVE ;
if ( status & 0x1a ) /* Input UV */
ret | = PB_STATUS_VIN_UV ;
if ( status & 0x40 ) /* OT */
ret | = PB_STATUS_TEMPERATURE ;
}
break ;
default :
ret = - ENODATA ;
}
return ret ;
}
static int pim4328_probe ( struct i2c_client * client )
{
int status ;
u8 device_id [ I2C_SMBUS_BLOCK_MAX + 1 ] ;
const struct i2c_device_id * mid ;
struct pim4328_data * data ;
struct pmbus_driver_info * info ;
struct pmbus_platform_data * pdata ;
struct device * dev = & client - > dev ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_READ_BYTE_DATA
| I2C_FUNC_SMBUS_BLOCK_DATA ) )
return - ENODEV ;
data = devm_kzalloc ( & client - > dev , sizeof ( struct pim4328_data ) ,
GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
status = i2c_smbus_read_block_data ( client , PMBUS_MFR_MODEL , device_id ) ;
if ( status < 0 ) {
dev_err ( & client - > dev , " Failed to read Manufacturer Model \n " ) ;
return status ;
}
for ( mid = pim4328_id ; mid - > name [ 0 ] ; mid + + ) {
if ( ! strncasecmp ( mid - > name , device_id , strlen ( mid - > name ) ) )
break ;
}
if ( ! mid - > name [ 0 ] ) {
dev_err ( & client - > dev , " Unsupported device \n " ) ;
return - ENODEV ;
}
if ( strcmp ( client - > name , mid - > name ) )
dev_notice ( & client - > dev ,
" Device mismatch: Configured %s, detected %s \n " ,
client - > name , mid - > name ) ;
data - > id = mid - > driver_data ;
info = & data - > info ;
info - > pages = 1 ;
info - > read_byte_data = pim4328_read_byte_data ;
info - > read_word_data = pim4328_read_word_data ;
pdata = devm_kzalloc ( dev , sizeof ( struct pmbus_platform_data ) ,
GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
dev - > platform_data = pdata ;
pdata - > flags = PMBUS_NO_CAPABILITY | PMBUS_NO_WRITE_PROTECT ;
switch ( data - > id ) {
case pim4006 :
info - > phases [ 0 ] = 2 ;
info - > func [ 0 ] = PMBUS_PHASE_VIRTUAL | PMBUS_HAVE_VIN
| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT ;
info - > pfunc [ 0 ] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN ;
info - > pfunc [ 1 ] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN ;
break ;
case pim4328 :
info - > phases [ 0 ] = 2 ;
info - > func [ 0 ] = PMBUS_PHASE_VIRTUAL
| PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN
| PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT ;
info - > pfunc [ 0 ] = PMBUS_HAVE_VIN ;
info - > pfunc [ 1 ] = PMBUS_HAVE_VIN ;
info - > format [ PSC_VOLTAGE_IN ] = direct ;
info - > format [ PSC_TEMPERATURE ] = direct ;
info - > format [ PSC_CURRENT_OUT ] = direct ;
pdata - > flags | = PMBUS_USE_COEFFICIENTS_CMD ;
break ;
case pim4820 :
info - > func [ 0 ] = PMBUS_HAVE_VIN | PMBUS_HAVE_TEMP
| PMBUS_HAVE_IIN ;
info - > format [ PSC_VOLTAGE_IN ] = direct ;
info - > format [ PSC_TEMPERATURE ] = direct ;
info - > format [ PSC_CURRENT_IN ] = direct ;
pdata - > flags | = PMBUS_USE_COEFFICIENTS_CMD ;
break ;
default :
return - ENODEV ;
}
return pmbus_do_probe ( client , info ) ;
}
static struct i2c_driver pim4328_driver = {
. driver = {
. name = " pim4328 " ,
} ,
2023-05-05 15:17:18 +02:00
. probe = pim4328_probe ,
2021-06-09 11:32:08 +02:00
. id_table = pim4328_id ,
} ;
module_i2c_driver ( pim4328_driver ) ;
MODULE_AUTHOR ( " Erik Rosen <erik.rosen@metormote.com> " ) ;
MODULE_DESCRIPTION ( " PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_IMPORT_NS ( PMBUS ) ;