2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-08-21 22:46:12 +03:00
/*
* Copyright 2017 IBM Corp .
*/
2019-10-09 22:11:02 +03:00
# include <linux/bitfield.h>
2017-08-21 22:46:12 +03:00
# include <linux/bitops.h>
2017-12-12 00:32:50 +03:00
# include <linux/debugfs.h>
2017-08-21 22:46:12 +03:00
# include <linux/device.h>
2017-12-12 00:32:50 +03:00
# include <linux/fs.h>
2017-08-21 22:46:12 +03:00
# include <linux/i2c.h>
2017-12-12 00:32:50 +03:00
# include <linux/jiffies.h>
2018-01-11 20:20:10 +03:00
# include <linux/leds.h>
2017-08-21 22:46:12 +03:00
# include <linux/module.h>
2017-12-12 00:32:50 +03:00
# include <linux/mutex.h>
2019-08-30 22:11:03 +03:00
# include <linux/of_device.h>
2018-01-09 00:10:09 +03:00
# include <linux/pmbus.h>
2017-08-21 22:46:12 +03:00
# include "pmbus.h"
2017-12-12 00:32:50 +03:00
# define CFFPS_CCIN_CMD 0xBD
2019-08-30 22:11:03 +03:00
# define CFFPS_FW_CMD 0xFA
# define CFFPS1_FW_NUM_BYTES 4
# define CFFPS2_FW_NUM_WORDS 3
2018-01-11 20:20:10 +03:00
# define CFFPS_SYS_CONFIG_CMD 0xDA
2019-12-19 23:50:06 +03:00
# define CFFPS_12VCS_VOUT_CMD 0xDE
2017-12-12 00:32:50 +03:00
# define CFFPS_INPUT_HISTORY_CMD 0xD6
2023-04-12 19:15:26 +03:00
# define CFFPS_INPUT_HISTORY_SIZE 101
2017-12-12 00:32:50 +03:00
2020-03-11 20:43:10 +03:00
# define CFFPS_CCIN_REVISION GENMASK(7, 0)
2021-10-04 17:43:38 +03:00
# define CFFPS_CCIN_REVISION_LEGACY 0xde
2019-10-09 22:11:02 +03:00
# define CFFPS_CCIN_VERSION GENMASK(15, 8)
# define CFFPS_CCIN_VERSION_1 0x2b
# define CFFPS_CCIN_VERSION_2 0x2e
2020-03-11 20:43:10 +03:00
# define CFFPS_CCIN_VERSION_3 0x51
2019-10-09 22:11:02 +03:00
2017-08-21 22:46:12 +03:00
/* STATUS_MFR_SPECIFIC bits */
# define CFFPS_MFR_FAN_FAULT BIT(0)
# define CFFPS_MFR_THERMAL_FAULT BIT(1)
# define CFFPS_MFR_OV_FAULT BIT(2)
# define CFFPS_MFR_UV_FAULT BIT(3)
# define CFFPS_MFR_PS_KILL BIT(4)
# define CFFPS_MFR_OC_FAULT BIT(5)
# define CFFPS_MFR_VAUX_FAULT BIT(6)
# define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7)
2021-08-07 01:51:31 +03:00
# define CFFPS_LED_BLINK (BIT(0) | BIT(6))
# define CFFPS_LED_ON (BIT(1) | BIT(6))
# define CFFPS_LED_OFF (BIT(2) | BIT(6))
2018-01-11 20:20:10 +03:00
# define CFFPS_BLINK_RATE_MS 250
2017-12-12 00:32:50 +03:00
enum {
2023-04-12 19:15:26 +03:00
CFFPS_DEBUGFS_MAX_POWER_OUT = 0 ,
2017-12-12 00:32:50 +03:00
CFFPS_DEBUGFS_CCIN ,
CFFPS_DEBUGFS_FW ,
2019-12-19 23:50:05 +03:00
CFFPS_DEBUGFS_ON_OFF_CONFIG ,
2017-12-12 00:32:50 +03:00
CFFPS_DEBUGFS_NUM_ENTRIES
} ;
2019-10-09 22:11:02 +03:00
enum versions { cffps1 , cffps2 , cffps_unknown } ;
2019-08-30 22:11:03 +03:00
2017-12-12 00:32:50 +03:00
struct ibm_cffps {
2019-08-30 22:11:03 +03:00
enum versions version ;
2017-12-12 00:32:50 +03:00
struct i2c_client * client ;
2023-04-12 19:15:26 +03:00
u8 input_history [ CFFPS_INPUT_HISTORY_SIZE ] ;
2017-12-12 00:32:50 +03:00
int debugfs_entries [ CFFPS_DEBUGFS_NUM_ENTRIES ] ;
2018-01-11 20:20:10 +03:00
char led_name [ 32 ] ;
u8 led_state ;
struct led_classdev led ;
2017-12-12 00:32:50 +03:00
} ;
# define to_psu(x, y) container_of((x), struct ibm_cffps, debugfs_entries[(y)])
2023-04-12 19:15:26 +03:00
static ssize_t ibm_cffps_debugfs_read_input_history ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
2017-12-12 00:32:50 +03:00
{
int rc ;
2023-04-12 19:15:26 +03:00
u8 cmd = CFFPS_INPUT_HISTORY_CMD ;
struct ibm_cffps * psu = file - > private_data ;
2017-12-12 00:32:50 +03:00
struct i2c_msg msg [ 2 ] = {
{
. addr = psu - > client - > addr ,
. flags = psu - > client - > flags ,
. len = 1 ,
2023-04-12 19:15:26 +03:00
. buf = & cmd ,
2017-12-12 00:32:50 +03:00
} , {
. addr = psu - > client - > addr ,
. flags = psu - > client - > flags | I2C_M_RD ,
2023-04-12 19:15:26 +03:00
. len = CFFPS_INPUT_HISTORY_SIZE ,
. buf = psu - > input_history ,
2017-12-12 00:32:50 +03:00
} ,
} ;
if ( ! * ppos ) {
2023-04-12 19:15:26 +03:00
rc = pmbus_lock_interruptible ( psu - > client ) ;
if ( rc )
return rc ;
2017-12-12 00:32:50 +03:00
2023-04-12 19:15:26 +03:00
rc = pmbus_set_page ( psu - > client , 0 , 0xff ) ;
if ( rc ) {
pmbus_unlock ( psu - > client ) ;
return rc ;
2017-12-12 00:32:50 +03:00
}
2023-04-12 19:15:26 +03:00
/*
* Use a raw i2c transfer , since we need more bytes
* than Linux I2C supports through smbus xfr ( only 32 ) .
*/
rc = i2c_transfer ( psu - > client - > adapter , msg , 2 ) ;
pmbus_unlock ( psu - > client ) ;
if ( rc < 0 )
return rc ;
2017-12-12 00:32:50 +03:00
}
return simple_read_from_buffer ( buf , count , ppos ,
2023-04-12 19:15:26 +03:00
psu - > input_history + 1 ,
psu - > input_history [ 0 ] ) ;
2017-12-12 00:32:50 +03:00
}
2023-04-12 19:15:26 +03:00
static const struct file_operations ibm_cffps_input_history_fops = {
. llseek = noop_llseek ,
. read = ibm_cffps_debugfs_read_input_history ,
. open = simple_open ,
} ;
2019-12-19 23:50:05 +03:00
static ssize_t ibm_cffps_debugfs_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
2017-12-12 00:32:50 +03:00
{
int i , rc ;
int * idxp = file - > private_data ;
int idx = * idxp ;
struct ibm_cffps * psu = to_psu ( idxp , idx ) ;
2019-12-19 23:50:05 +03:00
char data [ I2C_SMBUS_BLOCK_MAX + 2 ] = { 0 } ;
2017-12-12 00:32:50 +03:00
2023-04-12 19:15:26 +03:00
rc = pmbus_lock_interruptible ( psu - > client ) ;
if ( rc )
return rc ;
rc = pmbus_set_page ( psu - > client , 0 , 0xff ) ;
if ( rc )
goto unlock ;
2019-08-30 22:11:03 +03:00
2017-12-12 00:32:50 +03:00
switch ( idx ) {
2019-12-19 23:50:05 +03:00
case CFFPS_DEBUGFS_MAX_POWER_OUT :
2023-04-12 19:15:26 +03:00
if ( psu - > version = = cffps1 )
rc = i2c_smbus_read_word_swapped ( psu - > client , PMBUS_MFR_POUT_MAX ) ;
else
rc = i2c_smbus_read_word_data ( psu - > client , PMBUS_MFR_POUT_MAX ) ;
if ( rc > = 0 )
rc = snprintf ( data , I2C_SMBUS_BLOCK_MAX , " %d " , rc ) ;
break ;
2017-12-12 00:32:50 +03:00
case CFFPS_DEBUGFS_CCIN :
rc = i2c_smbus_read_word_swapped ( psu - > client , CFFPS_CCIN_CMD ) ;
2023-04-12 19:15:26 +03:00
if ( rc > = 0 )
rc = snprintf ( data , 5 , " %04X " , rc ) ;
break ;
2017-12-12 00:32:50 +03:00
case CFFPS_DEBUGFS_FW :
2019-08-30 22:11:03 +03:00
switch ( psu - > version ) {
case cffps1 :
for ( i = 0 ; i < CFFPS1_FW_NUM_BYTES ; + + i ) {
2023-04-12 19:15:26 +03:00
rc = i2c_smbus_read_byte_data ( psu - > client , CFFPS_FW_CMD + i ) ;
2019-08-30 22:11:03 +03:00
if ( rc < 0 )
2023-04-12 19:15:26 +03:00
goto unlock ;
2019-08-30 22:11:03 +03:00
snprintf ( & data [ i * 2 ] , 3 , " %02X " , rc ) ;
}
2017-12-12 00:32:50 +03:00
2019-08-30 22:11:03 +03:00
rc = i * 2 ;
break ;
case cffps2 :
for ( i = 0 ; i < CFFPS2_FW_NUM_WORDS ; + + i ) {
2023-04-12 19:15:26 +03:00
rc = i2c_smbus_read_word_data ( psu - > client , CFFPS_FW_CMD + i ) ;
2019-08-30 22:11:03 +03:00
if ( rc < 0 )
2023-04-12 19:15:26 +03:00
goto unlock ;
2019-08-30 22:11:03 +03:00
snprintf ( & data [ i * 4 ] , 5 , " %04X " , rc ) ;
}
2017-12-12 00:32:50 +03:00
2019-08-30 22:11:03 +03:00
rc = i * 4 ;
break ;
default :
2023-04-12 19:15:26 +03:00
rc = - EOPNOTSUPP ;
break ;
2019-08-30 22:11:03 +03:00
}
2023-04-12 19:15:26 +03:00
break ;
2019-12-19 23:50:05 +03:00
case CFFPS_DEBUGFS_ON_OFF_CONFIG :
2023-04-12 19:15:26 +03:00
rc = i2c_smbus_read_byte_data ( psu - > client , PMBUS_ON_OFF_CONFIG ) ;
if ( rc > = 0 )
rc = snprintf ( data , 3 , " %02x " , rc ) ;
break ;
2017-12-12 00:32:50 +03:00
default :
2023-04-12 19:15:26 +03:00
rc = - EINVAL ;
break ;
2017-12-12 00:32:50 +03:00
}
2023-04-12 19:15:26 +03:00
unlock :
pmbus_unlock ( psu - > client ) ;
2017-12-12 00:32:50 +03:00
if ( rc < 0 )
return rc ;
data [ rc ] = ' \n ' ;
rc + = 2 ;
return simple_read_from_buffer ( buf , count , ppos , data , rc ) ;
}
2019-12-19 23:50:05 +03:00
static ssize_t ibm_cffps_debugfs_write ( struct file * file ,
const char __user * buf , size_t count ,
loff_t * ppos )
{
u8 data ;
ssize_t rc ;
int * idxp = file - > private_data ;
int idx = * idxp ;
struct ibm_cffps * psu = to_psu ( idxp , idx ) ;
switch ( idx ) {
case CFFPS_DEBUGFS_ON_OFF_CONFIG :
rc = simple_write_to_buffer ( & data , 1 , ppos , buf , count ) ;
2020-01-07 18:40:40 +03:00
if ( rc < = 0 )
2019-12-19 23:50:05 +03:00
return rc ;
2023-04-12 19:15:26 +03:00
rc = pmbus_lock_interruptible ( psu - > client ) ;
if ( rc )
return rc ;
rc = pmbus_set_page ( psu - > client , 0 , 0xff ) ;
if ( rc ) {
pmbus_unlock ( psu - > client ) ;
return rc ;
}
rc = i2c_smbus_write_byte_data ( psu - > client , PMBUS_ON_OFF_CONFIG , data ) ;
pmbus_unlock ( psu - > client ) ;
2019-12-19 23:50:05 +03:00
if ( rc )
return rc ;
rc = 1 ;
break ;
default :
return - EINVAL ;
}
return rc ;
}
2017-12-12 00:32:50 +03:00
static const struct file_operations ibm_cffps_fops = {
. llseek = noop_llseek ,
2019-12-19 23:50:05 +03:00
. read = ibm_cffps_debugfs_read ,
. write = ibm_cffps_debugfs_write ,
2017-12-12 00:32:50 +03:00
. open = simple_open ,
} ;
2017-08-21 22:46:12 +03:00
static int ibm_cffps_read_byte_data ( struct i2c_client * client , int page ,
int reg )
{
int rc , mfr ;
switch ( reg ) {
case PMBUS_STATUS_VOUT :
case PMBUS_STATUS_IOUT :
case PMBUS_STATUS_TEMPERATURE :
case PMBUS_STATUS_FAN_12 :
rc = pmbus_read_byte_data ( client , page , reg ) ;
if ( rc < 0 )
return rc ;
mfr = pmbus_read_byte_data ( client , page ,
PMBUS_STATUS_MFR_SPECIFIC ) ;
if ( mfr < 0 )
/*
* Return the status register instead of an error ,
* since we successfully read status .
*/
return rc ;
/* Add MFR_SPECIFIC bits to the standard pmbus status regs. */
if ( reg = = PMBUS_STATUS_FAN_12 ) {
if ( mfr & CFFPS_MFR_FAN_FAULT )
rc | = PB_FAN_FAN1_FAULT ;
} else if ( reg = = PMBUS_STATUS_TEMPERATURE ) {
if ( mfr & CFFPS_MFR_THERMAL_FAULT )
rc | = PB_TEMP_OT_FAULT ;
} else if ( reg = = PMBUS_STATUS_VOUT ) {
if ( mfr & ( CFFPS_MFR_OV_FAULT | CFFPS_MFR_VAUX_FAULT ) )
rc | = PB_VOLTAGE_OV_FAULT ;
if ( mfr & CFFPS_MFR_UV_FAULT )
rc | = PB_VOLTAGE_UV_FAULT ;
} else if ( reg = = PMBUS_STATUS_IOUT ) {
if ( mfr & CFFPS_MFR_OC_FAULT )
rc | = PB_IOUT_OC_FAULT ;
if ( mfr & CFFPS_MFR_CURRENT_SHARE_WARNING )
rc | = PB_CURRENT_SHARE_FAULT ;
}
break ;
default :
rc = - ENODATA ;
break ;
}
return rc ;
}
static int ibm_cffps_read_word_data ( struct i2c_client * client , int page ,
2020-01-14 20:49:27 +03:00
int phase , int reg )
2017-08-21 22:46:12 +03:00
{
int rc , mfr ;
switch ( reg ) {
case PMBUS_STATUS_WORD :
2020-01-14 20:49:27 +03:00
rc = pmbus_read_word_data ( client , page , phase , reg ) ;
2017-08-21 22:46:12 +03:00
if ( rc < 0 )
return rc ;
mfr = pmbus_read_byte_data ( client , page ,
PMBUS_STATUS_MFR_SPECIFIC ) ;
if ( mfr < 0 )
/*
* Return the status register instead of an error ,
* since we successfully read status .
*/
return rc ;
if ( mfr & CFFPS_MFR_PS_KILL )
rc | = PB_STATUS_OFF ;
break ;
2019-12-19 23:50:06 +03:00
case PMBUS_VIRT_READ_VMON :
2020-01-14 20:49:27 +03:00
rc = pmbus_read_word_data ( client , page , phase ,
CFFPS_12VCS_VOUT_CMD ) ;
2019-12-19 23:50:06 +03:00
break ;
2017-08-21 22:46:12 +03:00
default :
rc = - ENODATA ;
break ;
}
return rc ;
}
2019-11-06 23:01:05 +03:00
static int ibm_cffps_led_brightness_set ( struct led_classdev * led_cdev ,
enum led_brightness brightness )
2018-01-11 20:20:10 +03:00
{
int rc ;
2019-11-06 23:01:06 +03:00
u8 next_led_state ;
2018-01-11 20:20:10 +03:00
struct ibm_cffps * psu = container_of ( led_cdev , struct ibm_cffps , led ) ;
if ( brightness = = LED_OFF ) {
2019-11-06 23:01:06 +03:00
next_led_state = CFFPS_LED_OFF ;
2018-01-11 20:20:10 +03:00
} else {
brightness = LED_FULL ;
2019-11-06 23:01:06 +03:00
2018-01-11 20:20:10 +03:00
if ( psu - > led_state ! = CFFPS_LED_BLINK )
2019-11-06 23:01:06 +03:00
next_led_state = CFFPS_LED_ON ;
else
next_led_state = CFFPS_LED_BLINK ;
2018-01-11 20:20:10 +03:00
}
2019-11-06 23:01:06 +03:00
dev_dbg ( & psu - > client - > dev , " LED brightness set: %d. Command: %d. \n " ,
brightness , next_led_state ) ;
2023-04-12 19:15:26 +03:00
rc = pmbus_lock_interruptible ( psu - > client ) ;
if ( rc )
return rc ;
rc = pmbus_set_page ( psu - > client , 0 , 0xff ) ;
if ( rc ) {
pmbus_unlock ( psu - > client ) ;
return rc ;
}
2019-08-30 22:11:03 +03:00
2018-01-11 20:20:10 +03:00
rc = i2c_smbus_write_byte_data ( psu - > client , CFFPS_SYS_CONFIG_CMD ,
2019-11-06 23:01:06 +03:00
next_led_state ) ;
2023-04-12 19:15:26 +03:00
pmbus_unlock ( psu - > client ) ;
2018-01-11 20:20:10 +03:00
if ( rc < 0 )
2019-11-06 23:01:05 +03:00
return rc ;
2018-01-11 20:20:10 +03:00
2019-11-06 23:01:06 +03:00
psu - > led_state = next_led_state ;
2018-01-11 20:20:10 +03:00
led_cdev - > brightness = brightness ;
2019-11-06 23:01:05 +03:00
return 0 ;
2018-01-11 20:20:10 +03:00
}
static int ibm_cffps_led_blink_set ( struct led_classdev * led_cdev ,
unsigned long * delay_on ,
unsigned long * delay_off )
{
int rc ;
struct ibm_cffps * psu = container_of ( led_cdev , struct ibm_cffps , led ) ;
2019-11-06 23:01:06 +03:00
dev_dbg ( & psu - > client - > dev , " LED blink set. \n " ) ;
2018-01-11 20:20:10 +03:00
2023-04-12 19:15:26 +03:00
rc = pmbus_lock_interruptible ( psu - > client ) ;
if ( rc )
return rc ;
rc = pmbus_set_page ( psu - > client , 0 , 0xff ) ;
if ( rc ) {
pmbus_unlock ( psu - > client ) ;
return rc ;
}
2019-08-30 22:11:03 +03:00
2018-01-11 20:20:10 +03:00
rc = i2c_smbus_write_byte_data ( psu - > client , CFFPS_SYS_CONFIG_CMD ,
CFFPS_LED_BLINK ) ;
2023-04-12 19:15:26 +03:00
pmbus_unlock ( psu - > client ) ;
2018-01-11 20:20:10 +03:00
if ( rc < 0 )
return rc ;
2019-11-06 23:01:06 +03:00
psu - > led_state = CFFPS_LED_BLINK ;
led_cdev - > brightness = LED_FULL ;
2018-01-11 20:20:10 +03:00
* delay_on = CFFPS_BLINK_RATE_MS ;
* delay_off = CFFPS_BLINK_RATE_MS ;
return 0 ;
}
static void ibm_cffps_create_led_class ( struct ibm_cffps * psu )
{
int rc ;
struct i2c_client * client = psu - > client ;
struct device * dev = & client - > dev ;
snprintf ( psu - > led_name , sizeof ( psu - > led_name ) , " %s-%02x " , client - > name ,
client - > addr ) ;
psu - > led . name = psu - > led_name ;
psu - > led . max_brightness = LED_FULL ;
2019-11-06 23:01:05 +03:00
psu - > led . brightness_set_blocking = ibm_cffps_led_brightness_set ;
2018-01-11 20:20:10 +03:00
psu - > led . blink_set = ibm_cffps_led_blink_set ;
rc = devm_led_classdev_register ( dev , & psu - > led ) ;
if ( rc )
dev_warn ( dev , " failed to register led class: %d \n " , rc ) ;
2019-12-19 23:50:07 +03:00
else
i2c_smbus_write_byte_data ( client , CFFPS_SYS_CONFIG_CMD ,
CFFPS_LED_OFF ) ;
2018-01-11 20:20:10 +03:00
}
2019-08-30 22:11:03 +03:00
static struct pmbus_driver_info ibm_cffps_info [ ] = {
[ cffps1 ] = {
. pages = 1 ,
. func [ 0 ] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
PMBUS_HAVE_STATUS_FAN12 ,
. read_byte_data = ibm_cffps_read_byte_data ,
. read_word_data = ibm_cffps_read_word_data ,
} ,
[ cffps2 ] = {
. pages = 2 ,
. func [ 0 ] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP |
PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
2019-12-19 23:50:06 +03:00
PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_VMON ,
2023-04-12 19:15:26 +03:00
. func [ 1 ] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
PMBUS_HAVE_STATUS_TEMP ,
2019-08-30 22:11:03 +03:00
. read_byte_data = ibm_cffps_read_byte_data ,
. read_word_data = ibm_cffps_read_word_data ,
} ,
2017-08-21 22:46:12 +03:00
} ;
2018-01-09 00:10:09 +03:00
static struct pmbus_platform_data ibm_cffps_pdata = {
2020-12-22 18:26:40 +03:00
. flags = PMBUS_SKIP_STATUS_CHECK | PMBUS_NO_CAPABILITY ,
2018-01-09 00:10:09 +03:00
} ;
2023-04-12 19:15:26 +03:00
static const struct i2c_device_id ibm_cffps_id [ ] = {
{ " ibm_cffps1 " , cffps1 } ,
{ " ibm_cffps2 " , cffps2 } ,
{ " ibm_cffps " , cffps_unknown } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ibm_cffps_id ) ;
2020-08-09 00:00:04 +03:00
static int ibm_cffps_probe ( struct i2c_client * client )
2017-08-21 22:46:12 +03:00
{
2017-12-12 00:32:50 +03:00
int i , rc ;
2019-10-09 22:11:02 +03:00
enum versions vs = cffps_unknown ;
2017-12-12 00:32:50 +03:00
struct dentry * debugfs ;
struct ibm_cffps * psu ;
2019-08-30 22:11:03 +03:00
const void * md = of_device_get_match_data ( & client - > dev ) ;
2020-08-09 00:00:04 +03:00
const struct i2c_device_id * id ;
2019-08-30 22:11:03 +03:00
2020-08-09 00:00:04 +03:00
if ( md ) {
2019-08-30 22:11:03 +03:00
vs = ( enum versions ) md ;
2020-08-09 00:00:04 +03:00
} else {
id = i2c_match_id ( ibm_cffps_id , client ) ;
if ( id )
vs = ( enum versions ) id - > driver_data ;
}
2019-10-09 22:11:02 +03:00
if ( vs = = cffps_unknown ) {
2020-03-11 20:43:10 +03:00
u16 ccin_revision = 0 ;
2019-10-09 22:11:02 +03:00
u16 ccin_version = CFFPS_CCIN_VERSION_1 ;
int ccin = i2c_smbus_read_word_swapped ( client , CFFPS_CCIN_CMD ) ;
2021-10-04 17:43:39 +03:00
char mfg_id [ I2C_SMBUS_BLOCK_MAX + 2 ] = { 0 } ;
2019-10-09 22:11:02 +03:00
2020-03-11 20:43:10 +03:00
if ( ccin > 0 ) {
ccin_revision = FIELD_GET ( CFFPS_CCIN_REVISION , ccin ) ;
2019-10-09 22:11:02 +03:00
ccin_version = FIELD_GET ( CFFPS_CCIN_VERSION , ccin ) ;
2020-03-11 20:43:10 +03:00
}
2019-10-09 22:11:02 +03:00
2021-10-04 17:43:39 +03:00
rc = i2c_smbus_read_block_data ( client , PMBUS_MFR_ID , mfg_id ) ;
if ( rc < 0 ) {
dev_err ( & client - > dev , " Failed to read Manufacturer ID \n " ) ;
return rc ;
}
2019-10-09 22:11:02 +03:00
switch ( ccin_version ) {
default :
case CFFPS_CCIN_VERSION_1 :
2021-10-04 17:43:39 +03:00
if ( ( strncmp ( mfg_id , " ACBE " , 4 ) = = 0 ) | |
( strncmp ( mfg_id , " ARTE " , 4 ) = = 0 ) )
vs = cffps1 ;
else
vs = cffps2 ;
2019-10-09 22:11:02 +03:00
break ;
case CFFPS_CCIN_VERSION_2 :
vs = cffps2 ;
break ;
2020-03-11 20:43:10 +03:00
case CFFPS_CCIN_VERSION_3 :
if ( ccin_revision = = CFFPS_CCIN_REVISION_LEGACY )
vs = cffps1 ;
else
vs = cffps2 ;
break ;
2019-10-09 22:11:02 +03:00
}
/* Set the client name to include the version number. */
snprintf ( client - > name , I2C_NAME_SIZE , " cffps%d " , vs + 1 ) ;
}
2017-12-12 00:32:50 +03:00
2018-01-09 00:10:09 +03:00
client - > dev . platform_data = & ibm_cffps_pdata ;
2020-08-09 00:00:04 +03:00
rc = pmbus_do_probe ( client , & ibm_cffps_info [ vs ] ) ;
2017-12-12 00:32:50 +03:00
if ( rc )
return rc ;
2018-01-11 20:20:10 +03:00
/*
* Don ' t fail the probe if there isn ' t enough memory for leds and
* debugfs .
*/
psu = devm_kzalloc ( & client - > dev , sizeof ( * psu ) , GFP_KERNEL ) ;
if ( ! psu )
return 0 ;
2019-08-30 22:11:03 +03:00
psu - > version = vs ;
2018-01-11 20:20:10 +03:00
psu - > client = client ;
ibm_cffps_create_led_class ( psu ) ;
2017-12-12 00:32:50 +03:00
/* Don't fail the probe if we can't create debugfs */
debugfs = pmbus_get_debugfs_dir ( client ) ;
if ( ! debugfs )
return 0 ;
for ( i = 0 ; i < CFFPS_DEBUGFS_NUM_ENTRIES ; + + i )
psu - > debugfs_entries [ i ] = i ;
2023-04-12 19:15:26 +03:00
debugfs_create_file ( " input_history " , 0444 , debugfs , psu , & ibm_cffps_input_history_fops ) ;
debugfs_create_file ( " max_power_out " , 0444 , debugfs ,
2019-12-19 23:50:05 +03:00
& psu - > debugfs_entries [ CFFPS_DEBUGFS_MAX_POWER_OUT ] ,
& ibm_cffps_fops ) ;
2023-04-12 19:15:26 +03:00
debugfs_create_file ( " ccin " , 0444 , debugfs ,
2017-12-12 00:32:50 +03:00
& psu - > debugfs_entries [ CFFPS_DEBUGFS_CCIN ] ,
& ibm_cffps_fops ) ;
2023-04-12 19:15:26 +03:00
debugfs_create_file ( " fw_version " , 0444 , debugfs ,
2017-12-12 00:32:50 +03:00
& psu - > debugfs_entries [ CFFPS_DEBUGFS_FW ] ,
& ibm_cffps_fops ) ;
2023-04-12 19:15:26 +03:00
debugfs_create_file ( " on_off_config " , 0644 , debugfs ,
2019-12-19 23:50:05 +03:00
& psu - > debugfs_entries [ CFFPS_DEBUGFS_ON_OFF_CONFIG ] ,
& ibm_cffps_fops ) ;
2017-12-12 00:32:50 +03:00
2023-04-12 19:15:26 +03:00
/* For compatibility with users of the old naming scheme. */
debugfs_create_symlink ( client - > name , debugfs , " . " ) ;
2017-12-12 00:32:50 +03:00
return 0 ;
2017-08-21 22:46:12 +03:00
}
static const struct of_device_id ibm_cffps_of_match [ ] = {
2019-08-30 22:11:03 +03:00
{
. compatible = " ibm,cffps1 " ,
. data = ( void * ) cffps1
} ,
{
. compatible = " ibm,cffps2 " ,
. data = ( void * ) cffps2
} ,
2019-10-09 22:11:02 +03:00
{
. compatible = " ibm,cffps " ,
. data = ( void * ) cffps_unknown
} ,
2017-08-21 22:46:12 +03:00
{ }
} ;
MODULE_DEVICE_TABLE ( of , ibm_cffps_of_match ) ;
static struct i2c_driver ibm_cffps_driver = {
. driver = {
. name = " ibm-cffps " ,
. of_match_table = ibm_cffps_of_match ,
} ,
2020-08-09 00:00:04 +03:00
. probe_new = ibm_cffps_probe ,
2017-08-21 22:46:12 +03:00
. id_table = ibm_cffps_id ,
} ;
module_i2c_driver ( ibm_cffps_driver ) ;
MODULE_AUTHOR ( " Eddie James " ) ;
MODULE_DESCRIPTION ( " PMBus driver for IBM Common Form Factor power supplies " ) ;
MODULE_LICENSE ( " GPL " ) ;
2021-04-20 09:07:07 +03:00
MODULE_IMPORT_NS ( PMBUS ) ;