2018-11-09 00:05:21 +03:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/device.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/fs.h>
# include <linux/fsi-sbefifo.h>
# include <linux/gfp.h>
# include <linux/idr.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/miscdevice.h>
2021-10-19 23:53:04 +03:00
# include <linux/mm.h>
2018-11-09 00:05:21 +03:00
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/fsi-occ.h>
# include <linux/of.h>
2023-07-18 23:55:08 +03:00
# include <linux/of_platform.h>
2018-11-09 00:05:21 +03:00
# include <linux/platform_device.h>
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
# include <asm/unaligned.h>
# define OCC_SRAM_BYTES 4096
# define OCC_CMD_DATA_BYTES 4090
# define OCC_RESP_DATA_BYTES 4089
2020-11-20 04:03:14 +03:00
# define OCC_P9_SRAM_CMD_ADDR 0xFFFBE000
# define OCC_P9_SRAM_RSP_ADDR 0xFFFBF000
# define OCC_P10_SRAM_CMD_ADDR 0xFFFFD000
# define OCC_P10_SRAM_RSP_ADDR 0xFFFFE000
# define OCC_P10_SRAM_MODE 0x58 /* Normal mode, OCB channel 2 */
2018-11-09 00:05:21 +03:00
# define OCC_TIMEOUT_MS 1000
# define OCC_CMD_IN_PRG_WAIT_MS 50
2020-11-20 04:03:14 +03:00
enum versions { occ_p9 , occ_p10 } ;
2018-11-09 00:05:21 +03:00
struct occ {
struct device * dev ;
struct device * sbefifo ;
char name [ 32 ] ;
int idx ;
2022-08-09 23:07:00 +03:00
bool platform_hwmon ;
2021-07-21 22:02:29 +03:00
u8 sequence_number ;
2021-10-19 23:53:04 +03:00
void * buffer ;
2021-10-19 23:53:05 +03:00
void * client_buffer ;
size_t client_buffer_size ;
size_t client_response_size ;
2020-11-20 04:03:14 +03:00
enum versions version ;
2018-11-09 00:05:21 +03:00
struct miscdevice mdev ;
struct mutex occ_lock ;
} ;
# define to_occ(x) container_of((x), struct occ, mdev)
struct occ_response {
u8 seq_no ;
u8 cmd_type ;
u8 return_status ;
__be16 data_length ;
u8 data [ OCC_RESP_DATA_BYTES + 2 ] ; /* two bytes checksum */
} __packed ;
struct occ_client {
struct occ * occ ;
struct mutex lock ;
size_t data_size ;
size_t read_offset ;
u8 * buffer ;
} ;
# define to_client(x) container_of((x), struct occ_client, xfr)
static DEFINE_IDA ( occ_ida ) ;
static int occ_open ( struct inode * inode , struct file * file )
{
struct occ_client * client = kzalloc ( sizeof ( * client ) , GFP_KERNEL ) ;
struct miscdevice * mdev = file - > private_data ;
struct occ * occ = to_occ ( mdev ) ;
if ( ! client )
return - ENOMEM ;
client - > buffer = ( u8 * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! client - > buffer ) {
kfree ( client ) ;
return - ENOMEM ;
}
client - > occ = occ ;
mutex_init ( & client - > lock ) ;
file - > private_data = client ;
2022-05-13 22:44:24 +03:00
get_device ( occ - > dev ) ;
2018-11-09 00:05:21 +03:00
/* We allocate a 1-page buffer, make sure it all fits */
BUILD_BUG_ON ( ( OCC_CMD_DATA_BYTES + 3 ) > PAGE_SIZE ) ;
BUILD_BUG_ON ( ( OCC_RESP_DATA_BYTES + 7 ) > PAGE_SIZE ) ;
return 0 ;
}
static ssize_t occ_read ( struct file * file , char __user * buf , size_t len ,
loff_t * offset )
{
struct occ_client * client = file - > private_data ;
ssize_t rc = 0 ;
if ( ! client )
return - ENODEV ;
if ( len > OCC_SRAM_BYTES )
return - EINVAL ;
mutex_lock ( & client - > lock ) ;
/* This should not be possible ... */
if ( WARN_ON_ONCE ( client - > read_offset > client - > data_size ) ) {
rc = - EIO ;
goto done ;
}
/* Grab how much data we have to read */
rc = min ( len , client - > data_size - client - > read_offset ) ;
if ( copy_to_user ( buf , client - > buffer + client - > read_offset , rc ) )
rc = - EFAULT ;
else
client - > read_offset + = rc ;
done :
mutex_unlock ( & client - > lock ) ;
return rc ;
}
static ssize_t occ_write ( struct file * file , const char __user * buf ,
size_t len , loff_t * offset )
{
struct occ_client * client = file - > private_data ;
size_t rlen , data_length ;
2021-07-21 22:02:29 +03:00
ssize_t rc ;
2018-11-09 00:05:21 +03:00
u8 * cmd ;
if ( ! client )
return - ENODEV ;
if ( len > ( OCC_CMD_DATA_BYTES + 3 ) | | len < 3 )
return - EINVAL ;
mutex_lock ( & client - > lock ) ;
/* Construct the command */
cmd = client - > buffer ;
/*
* Copy the user command ( assume user data follows the occ command
* format )
* byte 0 : command type
* bytes 1 - 2 : data length ( msb first )
* bytes 3 - n : data
*/
if ( copy_from_user ( & cmd [ 1 ] , buf , len ) ) {
rc = - EFAULT ;
goto done ;
}
/* Extract data length */
data_length = ( cmd [ 2 ] < < 8 ) + cmd [ 3 ] ;
if ( data_length > OCC_CMD_DATA_BYTES ) {
rc = - EINVAL ;
goto done ;
}
2021-07-21 22:02:29 +03:00
/* Submit command; 4 bytes before the data and 2 bytes after */
2018-11-09 00:05:21 +03:00
rlen = PAGE_SIZE ;
rc = fsi_occ_submit ( client - > occ - > dev , cmd , data_length + 6 , cmd ,
& rlen ) ;
if ( rc )
goto done ;
/* Set read tracking data */
client - > data_size = rlen ;
client - > read_offset = 0 ;
/* Done */
rc = len ;
done :
mutex_unlock ( & client - > lock ) ;
return rc ;
}
static int occ_release ( struct inode * inode , struct file * file )
{
struct occ_client * client = file - > private_data ;
2022-05-13 22:44:24 +03:00
put_device ( client - > occ - > dev ) ;
2018-11-09 00:05:21 +03:00
free_page ( ( unsigned long ) client - > buffer ) ;
kfree ( client ) ;
return 0 ;
}
static const struct file_operations occ_fops = {
. owner = THIS_MODULE ,
. open = occ_open ,
. read = occ_read ,
. write = occ_write ,
. release = occ_release ,
} ;
2021-10-19 23:53:05 +03:00
static void occ_save_ffdc ( struct occ * occ , __be32 * resp , size_t parsed_len ,
size_t resp_len )
{
if ( resp_len > parsed_len ) {
size_t dh = resp_len - parsed_len ;
size_t ffdc_len = ( dh - 1 ) * 4 ; /* SBE words are four bytes */
__be32 * ffdc = & resp [ parsed_len ] ;
if ( ffdc_len > occ - > client_buffer_size )
ffdc_len = occ - > client_buffer_size ;
memcpy ( occ - > client_buffer , ffdc , ffdc_len ) ;
occ - > client_response_size = ffdc_len ;
}
}
2021-02-09 20:12:33 +03:00
static int occ_verify_checksum ( struct occ * occ , struct occ_response * resp ,
u16 data_length )
2018-11-09 00:05:21 +03:00
{
/* Fetch the two bytes after the data for the checksum. */
u16 checksum_resp = get_unaligned_be16 ( & resp - > data [ data_length ] ) ;
u16 checksum ;
u16 i ;
checksum = resp - > seq_no ;
checksum + = resp - > cmd_type ;
checksum + = resp - > return_status ;
checksum + = ( data_length > > 8 ) + ( data_length & 0xFF ) ;
for ( i = 0 ; i < data_length ; + + i )
checksum + = resp - > data [ i ] ;
2021-02-09 20:12:33 +03:00
if ( checksum ! = checksum_resp ) {
dev_err ( occ - > dev , " Bad checksum: %04x!=%04x \n " , checksum ,
checksum_resp ) ;
2022-04-26 18:49:55 +03:00
return - EBADE ;
2021-02-09 20:12:33 +03:00
}
2018-11-09 00:05:21 +03:00
return 0 ;
}
2020-11-20 04:03:14 +03:00
static int occ_getsram ( struct occ * occ , u32 offset , void * data , ssize_t len )
2018-11-09 00:05:21 +03:00
{
u32 data_len = ( ( len + 7 ) / 8 ) * 8 ; /* must be multiples of 8 B */
2021-10-19 23:53:05 +03:00
size_t cmd_len , parsed_len , resp_data_len ;
2021-10-19 23:53:04 +03:00
size_t resp_len = OCC_MAX_RESP_WORDS ;
__be32 * resp = occ - > buffer ;
__be32 cmd [ 6 ] ;
2020-11-20 04:03:14 +03:00
int idx = 0 , rc ;
2018-11-09 00:05:21 +03:00
/*
* Magic sequence to do SBE getsram command . SBE will fetch data from
* specified SRAM address .
*/
2020-11-20 04:03:14 +03:00
switch ( occ - > version ) {
default :
case occ_p9 :
cmd_len = 5 ;
cmd [ 2 ] = cpu_to_be32 ( 1 ) ; /* Normal mode */
cmd [ 3 ] = cpu_to_be32 ( OCC_P9_SRAM_RSP_ADDR + offset ) ;
break ;
case occ_p10 :
idx = 1 ;
cmd_len = 6 ;
cmd [ 2 ] = cpu_to_be32 ( OCC_P10_SRAM_MODE ) ;
cmd [ 3 ] = 0 ;
cmd [ 4 ] = cpu_to_be32 ( OCC_P10_SRAM_RSP_ADDR + offset ) ;
break ;
}
cmd [ 0 ] = cpu_to_be32 ( cmd_len ) ;
2018-11-09 00:05:21 +03:00
cmd [ 1 ] = cpu_to_be32 ( SBEFIFO_CMD_GET_OCC_SRAM ) ;
2020-11-20 04:03:14 +03:00
cmd [ 4 + idx ] = cpu_to_be32 ( data_len ) ;
2018-11-09 00:05:21 +03:00
2020-11-20 04:03:14 +03:00
rc = sbefifo_submit ( occ - > sbefifo , cmd , cmd_len , resp , & resp_len ) ;
2018-11-09 00:05:21 +03:00
if ( rc )
2021-10-19 23:53:04 +03:00
return rc ;
2018-11-09 00:05:21 +03:00
rc = sbefifo_parse_status ( occ - > sbefifo , SBEFIFO_CMD_GET_OCC_SRAM ,
2021-10-19 23:53:05 +03:00
resp , resp_len , & parsed_len ) ;
2021-10-19 23:53:04 +03:00
if ( rc > 0 ) {
dev_err ( occ - > dev , " SRAM read returned failure status: %08x \n " ,
rc ) ;
2021-10-19 23:53:05 +03:00
occ_save_ffdc ( occ , resp , parsed_len , resp_len ) ;
return - ECOMM ;
2021-10-19 23:53:04 +03:00
} else if ( rc ) {
return rc ;
}
2018-11-09 00:05:21 +03:00
2021-10-19 23:53:05 +03:00
resp_data_len = be32_to_cpu ( resp [ parsed_len - 1 ] ) ;
2018-11-09 00:05:21 +03:00
if ( resp_data_len ! = data_len ) {
dev_err ( occ - > dev , " SRAM read expected %d bytes got %zd \n " ,
data_len , resp_data_len ) ;
rc = - EBADMSG ;
} else {
memcpy ( data , resp , len ) ;
}
return rc ;
}
2021-07-21 22:02:29 +03:00
static int occ_putsram ( struct occ * occ , const void * data , ssize_t len ,
u8 seq_no , u16 checksum )
2018-11-09 00:05:21 +03:00
{
u32 data_len = ( ( len + 7 ) / 8 ) * 8 ; /* must be multiples of 8 B */
2021-10-19 23:53:05 +03:00
size_t cmd_len , parsed_len , resp_data_len ;
2021-10-19 23:53:04 +03:00
size_t resp_len = OCC_MAX_RESP_WORDS ;
__be32 * buf = occ - > buffer ;
2021-07-21 22:02:29 +03:00
u8 * byte_buf ;
2020-11-20 04:03:14 +03:00
int idx = 0 , rc ;
cmd_len = ( occ - > version = = occ_p10 ) ? 6 : 5 ;
cmd_len + = data_len > > 2 ;
2018-11-09 00:05:21 +03:00
/*
* Magic sequence to do SBE putsram command . SBE will transfer
* data to specified SRAM address .
*/
buf [ 0 ] = cpu_to_be32 ( cmd_len ) ;
buf [ 1 ] = cpu_to_be32 ( SBEFIFO_CMD_PUT_OCC_SRAM ) ;
2020-11-20 04:03:14 +03:00
switch ( occ - > version ) {
default :
case occ_p9 :
buf [ 2 ] = cpu_to_be32 ( 1 ) ; /* Normal mode */
buf [ 3 ] = cpu_to_be32 ( OCC_P9_SRAM_CMD_ADDR ) ;
break ;
case occ_p10 :
idx = 1 ;
buf [ 2 ] = cpu_to_be32 ( OCC_P10_SRAM_MODE ) ;
buf [ 3 ] = 0 ;
buf [ 4 ] = cpu_to_be32 ( OCC_P10_SRAM_CMD_ADDR ) ;
break ;
}
buf [ 4 + idx ] = cpu_to_be32 ( data_len ) ;
memcpy ( & buf [ 5 + idx ] , data , len ) ;
2018-11-09 00:05:21 +03:00
2021-07-21 22:02:29 +03:00
byte_buf = ( u8 * ) & buf [ 5 + idx ] ;
/*
* Overwrite the first byte with our sequence number and the last two
* bytes with the checksum .
*/
byte_buf [ 0 ] = seq_no ;
byte_buf [ len - 2 ] = checksum > > 8 ;
byte_buf [ len - 1 ] = checksum & 0xff ;
2018-11-09 00:05:21 +03:00
rc = sbefifo_submit ( occ - > sbefifo , buf , cmd_len , buf , & resp_len ) ;
if ( rc )
2021-10-19 23:53:04 +03:00
return rc ;
2018-11-09 00:05:21 +03:00
rc = sbefifo_parse_status ( occ - > sbefifo , SBEFIFO_CMD_PUT_OCC_SRAM ,
2021-10-19 23:53:05 +03:00
buf , resp_len , & parsed_len ) ;
2021-10-19 23:53:04 +03:00
if ( rc > 0 ) {
dev_err ( occ - > dev , " SRAM write returned failure status: %08x \n " ,
rc ) ;
2021-10-19 23:53:05 +03:00
occ_save_ffdc ( occ , buf , parsed_len , resp_len ) ;
return - ECOMM ;
2021-10-19 23:53:04 +03:00
} else if ( rc ) {
return rc ;
}
2018-11-09 00:05:21 +03:00
2021-10-19 23:53:05 +03:00
if ( parsed_len ! = 1 ) {
2018-11-09 00:05:21 +03:00
dev_err ( occ - > dev , " SRAM write response length invalid: %zd \n " ,
2021-10-19 23:53:05 +03:00
parsed_len ) ;
2018-11-09 00:05:21 +03:00
rc = - EBADMSG ;
} else {
resp_data_len = be32_to_cpu ( buf [ 0 ] ) ;
if ( resp_data_len ! = data_len ) {
dev_err ( occ - > dev ,
" SRAM write expected %d bytes got %zd \n " ,
data_len , resp_data_len ) ;
rc = - EBADMSG ;
}
}
return rc ;
}
static int occ_trigger_attn ( struct occ * occ )
{
2021-10-19 23:53:04 +03:00
__be32 * buf = occ - > buffer ;
2021-10-19 23:53:05 +03:00
size_t cmd_len , parsed_len , resp_data_len ;
2021-10-19 23:53:04 +03:00
size_t resp_len = OCC_MAX_RESP_WORDS ;
2020-11-20 04:03:14 +03:00
int idx = 0 , rc ;
2018-11-09 00:05:21 +03:00
2020-11-20 04:03:14 +03:00
switch ( occ - > version ) {
default :
case occ_p9 :
cmd_len = 7 ;
buf [ 2 ] = cpu_to_be32 ( 3 ) ; /* Circular mode */
buf [ 3 ] = 0 ;
break ;
case occ_p10 :
idx = 1 ;
cmd_len = 8 ;
buf [ 2 ] = cpu_to_be32 ( 0xd0 ) ; /* Circular mode, OCB Channel 1 */
buf [ 3 ] = 0 ;
buf [ 4 ] = 0 ;
break ;
}
buf [ 0 ] = cpu_to_be32 ( cmd_len ) ; /* Chip-op length in words */
2018-11-09 00:05:21 +03:00
buf [ 1 ] = cpu_to_be32 ( SBEFIFO_CMD_PUT_OCC_SRAM ) ;
2020-11-20 04:03:14 +03:00
buf [ 4 + idx ] = cpu_to_be32 ( 8 ) ; /* Data length in bytes */
buf [ 5 + idx ] = cpu_to_be32 ( 0x20010000 ) ; /* Trigger OCC attention */
buf [ 6 + idx ] = 0 ;
2018-11-09 00:05:21 +03:00
2020-11-20 04:03:14 +03:00
rc = sbefifo_submit ( occ - > sbefifo , buf , cmd_len , buf , & resp_len ) ;
2018-11-09 00:05:21 +03:00
if ( rc )
2021-10-19 23:53:04 +03:00
return rc ;
2018-11-09 00:05:21 +03:00
rc = sbefifo_parse_status ( occ - > sbefifo , SBEFIFO_CMD_PUT_OCC_SRAM ,
2021-10-19 23:53:05 +03:00
buf , resp_len , & parsed_len ) ;
2021-10-19 23:53:04 +03:00
if ( rc > 0 ) {
dev_err ( occ - > dev , " SRAM attn returned failure status: %08x \n " ,
rc ) ;
2021-10-19 23:53:05 +03:00
occ_save_ffdc ( occ , buf , parsed_len , resp_len ) ;
return - ECOMM ;
2021-10-19 23:53:04 +03:00
} else if ( rc ) {
return rc ;
}
2018-11-09 00:05:21 +03:00
2021-10-19 23:53:05 +03:00
if ( parsed_len ! = 1 ) {
2018-11-09 00:05:21 +03:00
dev_err ( occ - > dev , " SRAM attn response length invalid: %zd \n " ,
2021-10-19 23:53:05 +03:00
parsed_len ) ;
2018-11-09 00:05:21 +03:00
rc = - EBADMSG ;
} else {
resp_data_len = be32_to_cpu ( buf [ 0 ] ) ;
if ( resp_data_len ! = 8 ) {
dev_err ( occ - > dev ,
" SRAM attn expected 8 bytes got %zd \n " ,
resp_data_len ) ;
rc = - EBADMSG ;
}
}
return rc ;
}
2022-02-08 18:22:35 +03:00
static bool fsi_occ_response_not_ready ( struct occ_response * resp , u8 seq_no ,
u8 cmd_type )
{
return resp - > return_status = = OCC_RESP_CMD_IN_PRG | |
resp - > return_status = = OCC_RESP_CRIT_INIT | |
resp - > seq_no ! = seq_no | | resp - > cmd_type ! = cmd_type ;
}
2018-11-09 00:05:21 +03:00
int fsi_occ_submit ( struct device * dev , const void * request , size_t req_len ,
void * response , size_t * resp_len )
{
const unsigned long timeout = msecs_to_jiffies ( OCC_TIMEOUT_MS ) ;
const unsigned long wait_time =
msecs_to_jiffies ( OCC_CMD_IN_PRG_WAIT_MS ) ;
struct occ * occ = dev_get_drvdata ( dev ) ;
struct occ_response * resp = response ;
2021-10-19 23:53:05 +03:00
size_t user_resp_len = * resp_len ;
2019-07-02 18:47:42 +03:00
u8 seq_no ;
2022-02-08 18:22:35 +03:00
u8 cmd_type ;
2021-07-21 22:02:29 +03:00
u16 checksum = 0 ;
2018-11-09 00:05:21 +03:00
u16 resp_data_length ;
2021-07-21 22:02:29 +03:00
const u8 * byte_request = ( const u8 * ) request ;
2022-02-08 18:22:35 +03:00
unsigned long end ;
2018-11-09 00:05:21 +03:00
int rc ;
2021-07-21 22:02:29 +03:00
size_t i ;
2018-11-09 00:05:21 +03:00
2021-10-19 23:53:05 +03:00
* resp_len = 0 ;
2018-11-09 00:05:21 +03:00
if ( ! occ )
return - ENODEV ;
2021-10-19 23:53:05 +03:00
if ( user_resp_len < 7 ) {
dev_dbg ( dev , " Bad resplen %zd \n " , user_resp_len ) ;
2018-11-09 00:05:21 +03:00
return - EINVAL ;
}
2022-02-08 18:22:35 +03:00
cmd_type = byte_request [ 1 ] ;
2021-07-21 22:02:29 +03:00
/* Checksum the request, ignoring first byte (sequence number). */
for ( i = 1 ; i < req_len - 2 ; + + i )
checksum + = byte_request [ i ] ;
2022-05-13 22:44:24 +03:00
rc = mutex_lock_interruptible ( & occ - > occ_lock ) ;
if ( rc )
return rc ;
2018-11-09 00:05:21 +03:00
2021-10-19 23:53:05 +03:00
occ - > client_buffer = response ;
occ - > client_buffer_size = user_resp_len ;
occ - > client_response_size = 0 ;
2022-05-13 22:44:24 +03:00
if ( ! occ - > buffer ) {
rc = - ENOENT ;
goto done ;
}
2021-07-21 22:02:29 +03:00
/*
* Get a sequence number and update the counter . Avoid a sequence
* number of 0 which would pass the response check below even if the
* OCC response is uninitialized . Any sequence number the user is
* trying to send is overwritten since this function is the only common
* interface to the OCC and therefore the only place we can guarantee
* unique sequence numbers .
*/
seq_no = occ - > sequence_number + + ;
if ( ! occ - > sequence_number )
occ - > sequence_number = 1 ;
checksum + = seq_no ;
rc = occ_putsram ( occ , request , req_len , seq_no , checksum ) ;
2018-11-09 00:05:21 +03:00
if ( rc )
goto done ;
rc = occ_trigger_attn ( occ ) ;
if ( rc )
goto done ;
2022-02-08 18:22:35 +03:00
end = jiffies + timeout ;
while ( true ) {
/* Read occ response header */
2020-11-20 04:03:14 +03:00
rc = occ_getsram ( occ , 0 , resp , 8 ) ;
2018-11-09 00:05:21 +03:00
if ( rc )
goto done ;
2022-02-08 18:22:35 +03:00
if ( fsi_occ_response_not_ready ( resp , seq_no , cmd_type ) ) {
if ( time_after ( jiffies , end ) ) {
dev_err ( occ - > dev ,
" resp timeout status=%02x seq=%d cmd=%d, our seq=%d cmd=%d \n " ,
2019-07-02 18:47:42 +03:00
resp - > return_status , resp - > seq_no ,
2022-02-08 18:22:35 +03:00
resp - > cmd_type , seq_no , cmd_type ) ;
rc = - ETIMEDOUT ;
2019-07-02 18:47:42 +03:00
goto done ;
}
2018-11-09 00:05:21 +03:00
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
schedule_timeout ( wait_time ) ;
2022-02-08 18:22:35 +03:00
} else {
/* Extract size of response data */
resp_data_length =
get_unaligned_be16 ( & resp - > data_length ) ;
/*
* Message size is data length + 5 bytes header + 2
* bytes checksum
*/
if ( ( resp_data_length + 7 ) > user_resp_len ) {
rc = - EMSGSIZE ;
goto done ;
}
2018-11-09 00:05:21 +03:00
2022-02-08 18:22:35 +03:00
/*
* Get the entire response including the header again ,
* in case it changed
*/
if ( resp_data_length > 1 ) {
rc = occ_getsram ( occ , 0 , resp ,
resp_data_length + 7 ) ;
if ( rc )
goto done ;
if ( ! fsi_occ_response_not_ready ( resp , seq_no ,
cmd_type ) )
break ;
} else {
break ;
}
}
2018-11-09 00:05:21 +03:00
}
dev_dbg ( dev , " resp_status=%02x resp_data_len=%d \n " ,
resp - > return_status , resp_data_length ) ;
2021-02-09 20:12:33 +03:00
rc = occ_verify_checksum ( occ , resp , resp_data_length ) ;
2022-04-26 18:49:55 +03:00
if ( rc )
goto done ;
occ - > client_response_size = resp_data_length + 7 ;
2018-11-09 00:05:21 +03:00
done :
2021-10-19 23:53:05 +03:00
* resp_len = occ - > client_response_size ;
2018-11-09 00:05:21 +03:00
mutex_unlock ( & occ - > occ_lock ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( fsi_occ_submit ) ;
2022-08-09 23:07:00 +03:00
static int occ_unregister_platform_child ( struct device * dev , void * data )
2018-11-09 00:05:21 +03:00
{
struct platform_device * hwmon_dev = to_platform_device ( dev ) ;
platform_device_unregister ( hwmon_dev ) ;
return 0 ;
}
2022-08-09 23:07:00 +03:00
static int occ_unregister_of_child ( struct device * dev , void * data )
{
struct platform_device * hwmon_dev = to_platform_device ( dev ) ;
of_device_unregister ( hwmon_dev ) ;
if ( dev - > of_node )
of_node_clear_flag ( dev - > of_node , OF_POPULATED ) ;
return 0 ;
}
2018-11-09 00:05:21 +03:00
static int occ_probe ( struct platform_device * pdev )
{
int rc ;
u32 reg ;
2022-08-09 23:07:00 +03:00
char child_name [ 32 ] ;
2018-11-09 00:05:21 +03:00
struct occ * occ ;
2022-08-09 23:07:00 +03:00
struct platform_device * hwmon_dev = NULL ;
struct device_node * hwmon_node ;
2018-11-09 00:05:21 +03:00
struct device * dev = & pdev - > dev ;
struct platform_device_info hwmon_dev_info = {
. parent = dev ,
. name = " occ-hwmon " ,
} ;
occ = devm_kzalloc ( dev , sizeof ( * occ ) , GFP_KERNEL ) ;
if ( ! occ )
return - ENOMEM ;
2021-10-19 23:53:04 +03:00
/* SBE words are always four bytes */
occ - > buffer = kvmalloc ( OCC_MAX_RESP_WORDS * 4 , GFP_KERNEL ) ;
if ( ! occ - > buffer )
return - ENOMEM ;
2020-11-20 04:03:14 +03:00
occ - > version = ( uintptr_t ) of_device_get_match_data ( dev ) ;
2018-11-09 00:05:21 +03:00
occ - > dev = dev ;
occ - > sbefifo = dev - > parent ;
2022-02-08 18:22:35 +03:00
/*
* Quickly derive a pseudo - random number from jiffies so that
* re - probing the driver doesn ' t accidentally overlap sequence numbers .
*/
occ - > sequence_number = ( u8 ) ( ( jiffies % 0xff ) + 1 ) ;
2018-11-09 00:05:21 +03:00
mutex_init ( & occ - > occ_lock ) ;
if ( dev - > of_node ) {
rc = of_property_read_u32 ( dev - > of_node , " reg " , & reg ) ;
if ( ! rc ) {
/* make sure we don't have a duplicate from dts */
occ - > idx = ida_simple_get ( & occ_ida , reg , reg + 1 ,
GFP_KERNEL ) ;
if ( occ - > idx < 0 )
occ - > idx = ida_simple_get ( & occ_ida , 1 , INT_MAX ,
GFP_KERNEL ) ;
} else {
occ - > idx = ida_simple_get ( & occ_ida , 1 , INT_MAX ,
GFP_KERNEL ) ;
}
} else {
occ - > idx = ida_simple_get ( & occ_ida , 1 , INT_MAX , GFP_KERNEL ) ;
}
platform_set_drvdata ( pdev , occ ) ;
snprintf ( occ - > name , sizeof ( occ - > name ) , " occ%d " , occ - > idx ) ;
occ - > mdev . fops = & occ_fops ;
occ - > mdev . minor = MISC_DYNAMIC_MINOR ;
occ - > mdev . name = occ - > name ;
occ - > mdev . parent = dev ;
rc = misc_register ( & occ - > mdev ) ;
if ( rc ) {
dev_err ( dev , " failed to register miscdevice: %d \n " , rc ) ;
ida_simple_remove ( & occ_ida , occ - > idx ) ;
2021-10-19 23:53:04 +03:00
kvfree ( occ - > buffer ) ;
2018-11-09 00:05:21 +03:00
return rc ;
}
2022-08-09 23:07:00 +03:00
hwmon_node = of_get_child_by_name ( dev - > of_node , hwmon_dev_info . name ) ;
if ( hwmon_node ) {
snprintf ( child_name , sizeof ( child_name ) , " %s.%d " , hwmon_dev_info . name , occ - > idx ) ;
hwmon_dev = of_platform_device_create ( hwmon_node , child_name , dev ) ;
of_node_put ( hwmon_node ) ;
}
if ( ! hwmon_dev ) {
occ - > platform_hwmon = true ;
hwmon_dev_info . id = occ - > idx ;
hwmon_dev = platform_device_register_full ( & hwmon_dev_info ) ;
if ( IS_ERR ( hwmon_dev ) )
dev_warn ( dev , " failed to create hwmon device \n " ) ;
}
2018-11-09 00:05:21 +03:00
return 0 ;
}
static int occ_remove ( struct platform_device * pdev )
{
struct occ * occ = platform_get_drvdata ( pdev ) ;
misc_deregister ( & occ - > mdev ) ;
2022-05-13 22:44:24 +03:00
mutex_lock ( & occ - > occ_lock ) ;
kvfree ( occ - > buffer ) ;
occ - > buffer = NULL ;
mutex_unlock ( & occ - > occ_lock ) ;
2022-08-09 23:07:00 +03:00
if ( occ - > platform_hwmon )
device_for_each_child ( & pdev - > dev , NULL , occ_unregister_platform_child ) ;
else
device_for_each_child ( & pdev - > dev , NULL , occ_unregister_of_child ) ;
2018-11-09 00:05:21 +03:00
ida_simple_remove ( & occ_ida , occ - > idx ) ;
return 0 ;
}
static const struct of_device_id occ_match [ ] = {
2020-11-20 04:03:14 +03:00
{
. compatible = " ibm,p9-occ " ,
. data = ( void * ) occ_p9
} ,
{
. compatible = " ibm,p10-occ " ,
. data = ( void * ) occ_p10
} ,
2018-11-09 00:05:21 +03:00
{ } ,
} ;
2021-05-13 11:57:29 +03:00
MODULE_DEVICE_TABLE ( of , occ_match ) ;
2018-11-09 00:05:21 +03:00
static struct platform_driver occ_driver = {
. driver = {
. name = " occ " ,
. of_match_table = occ_match ,
} ,
. probe = occ_probe ,
. remove = occ_remove ,
} ;
static int occ_init ( void )
{
return platform_driver_register ( & occ_driver ) ;
}
static void occ_exit ( void )
{
platform_driver_unregister ( & occ_driver ) ;
ida_destroy ( & occ_ida ) ;
}
module_init ( occ_init ) ;
module_exit ( occ_exit ) ;
MODULE_AUTHOR ( " Eddie James <eajames@linux.ibm.com> " ) ;
MODULE_DESCRIPTION ( " BMC P9 OCC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;