2018-11-13 21:14:04 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* FPGA Manager Driver for Intel Stratix10 SoC
*
* Copyright ( C ) 2018 Intel Corporation
*/
# include <linux/completion.h>
# include <linux/fpga/fpga-mgr.h>
# include <linux/firmware/intel/stratix10-svc-client.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_platform.h>
/*
* FPGA programming requires a higher level of privilege ( EL3 ) , per the SoC
* design .
*/
# define NUM_SVC_BUFS 4
# define SVC_BUF_SIZE SZ_512K
/* Indicates buffer is in use if set */
# define SVC_BUF_LOCK 0
# define S10_BUFFER_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_BUFFER_TIMEOUT_MS))
# define S10_RECONFIG_TIMEOUT (msecs_to_jiffies(SVC_RECONFIG_REQUEST_TIMEOUT_MS))
/*
* struct s10_svc_buf
* buf : virtual address of buf provided by service layer
* lock : locked if buffer is in use
*/
struct s10_svc_buf {
char * buf ;
unsigned long lock ;
} ;
struct s10_priv {
struct stratix10_svc_chan * chan ;
struct stratix10_svc_client client ;
struct completion status_return_completion ;
struct s10_svc_buf svc_bufs [ NUM_SVC_BUFS ] ;
unsigned long status ;
} ;
static int s10_svc_send_msg ( struct s10_priv * priv ,
enum stratix10_svc_command_code command ,
void * payload , u32 payload_length )
{
struct stratix10_svc_chan * chan = priv - > chan ;
struct device * dev = priv - > client . dev ;
struct stratix10_svc_client_msg msg ;
int ret ;
dev_dbg ( dev , " %s cmd=%d payload=%p length=%d \n " ,
__func__ , command , payload , payload_length ) ;
msg . command = command ;
msg . payload = payload ;
msg . payload_length = payload_length ;
ret = stratix10_svc_send ( chan , & msg ) ;
dev_dbg ( dev , " stratix10_svc_send returned status %d \n " , ret ) ;
return ret ;
}
/*
* Free buffers allocated from the service layer ' s pool that are not in use .
* Return true when all buffers are freed .
*/
static bool s10_free_buffers ( struct fpga_manager * mgr )
{
struct s10_priv * priv = mgr - > priv ;
uint num_free = 0 ;
uint i ;
for ( i = 0 ; i < NUM_SVC_BUFS ; i + + ) {
if ( ! priv - > svc_bufs [ i ] . buf ) {
num_free + + ;
continue ;
}
if ( ! test_and_set_bit_lock ( SVC_BUF_LOCK ,
& priv - > svc_bufs [ i ] . lock ) ) {
stratix10_svc_free_memory ( priv - > chan ,
priv - > svc_bufs [ i ] . buf ) ;
priv - > svc_bufs [ i ] . buf = NULL ;
num_free + + ;
}
}
return num_free = = NUM_SVC_BUFS ;
}
/*
* Returns count of how many buffers are not in use .
*/
static uint s10_free_buffer_count ( struct fpga_manager * mgr )
{
struct s10_priv * priv = mgr - > priv ;
uint num_free = 0 ;
uint i ;
for ( i = 0 ; i < NUM_SVC_BUFS ; i + + )
if ( ! priv - > svc_bufs [ i ] . buf )
num_free + + ;
return num_free ;
}
/*
* s10_unlock_bufs
* Given the returned buffer address , match that address to our buffer struct
* and unlock that buffer . This marks it as available to be refilled and sent
* ( or freed ) .
* priv : private data
* kaddr : kernel address of buffer that was returned from service layer
*/
static void s10_unlock_bufs ( struct s10_priv * priv , void * kaddr )
{
uint i ;
if ( ! kaddr )
return ;
for ( i = 0 ; i < NUM_SVC_BUFS ; i + + )
if ( priv - > svc_bufs [ i ] . buf = = kaddr ) {
clear_bit_unlock ( SVC_BUF_LOCK ,
& priv - > svc_bufs [ i ] . lock ) ;
return ;
}
WARN ( 1 , " Unknown buffer returned from service layer %p \n " , kaddr ) ;
}
/*
* s10_receive_callback - callback for service layer to use to provide client
* ( this driver ) messages received through the mailbox .
* client : service layer client struct
* data : message from service layer
*/
static void s10_receive_callback ( struct stratix10_svc_client * client ,
struct stratix10_svc_cb_data * data )
{
struct s10_priv * priv = client - > priv ;
u32 status ;
int i ;
WARN_ONCE ( ! data , " %s: stratix10_svc_rc_data = NULL " , __func__ ) ;
status = data - > status ;
/*
* Here we set status bits as we receive them . Elsewhere , we always use
* test_and_clear_bit ( ) to check status in priv - > status
*/
for ( i = 0 ; i < = SVC_STATUS_RECONFIG_ERROR ; i + + )
if ( status & ( 1 < < i ) )
set_bit ( i , & priv - > status ) ;
if ( status & BIT ( SVC_STATUS_RECONFIG_BUFFER_DONE ) ) {
s10_unlock_bufs ( priv , data - > kaddr1 ) ;
s10_unlock_bufs ( priv , data - > kaddr2 ) ;
s10_unlock_bufs ( priv , data - > kaddr3 ) ;
}
complete ( & priv - > status_return_completion ) ;
}
/*
* s10_ops_write_init - prepare for FPGA reconfiguration by requesting
* partial reconfig and allocating buffers from the service layer .
*/
static int s10_ops_write_init ( struct fpga_manager * mgr ,
struct fpga_image_info * info ,
const char * buf , size_t count )
{
struct s10_priv * priv = mgr - > priv ;
struct device * dev = priv - > client . dev ;
struct stratix10_svc_command_config_type ctype ;
char * kbuf ;
uint i ;
int ret ;
ctype . flags = 0 ;
if ( info - > flags & FPGA_MGR_PARTIAL_RECONFIG ) {
dev_dbg ( dev , " Requesting partial reconfiguration. \n " ) ;
ctype . flags | = BIT ( COMMAND_RECONFIG_FLAG_PARTIAL ) ;
} else {
dev_dbg ( dev , " Requesting full reconfiguration. \n " ) ;
}
reinit_completion ( & priv - > status_return_completion ) ;
ret = s10_svc_send_msg ( priv , COMMAND_RECONFIG ,
& ctype , sizeof ( ctype ) ) ;
if ( ret < 0 )
goto init_done ;
ret = wait_for_completion_interruptible_timeout (
& priv - > status_return_completion , S10_RECONFIG_TIMEOUT ) ;
if ( ! ret ) {
dev_err ( dev , " timeout waiting for RECONFIG_REQUEST \n " ) ;
ret = - ETIMEDOUT ;
goto init_done ;
}
if ( ret < 0 ) {
dev_err ( dev , " error (%d) waiting for RECONFIG_REQUEST \n " , ret ) ;
goto init_done ;
}
ret = 0 ;
if ( ! test_and_clear_bit ( SVC_STATUS_RECONFIG_REQUEST_OK ,
& priv - > status ) ) {
ret = - ETIMEDOUT ;
goto init_done ;
}
/* Allocate buffers from the service layer's pool. */
for ( i = 0 ; i < NUM_SVC_BUFS ; i + + ) {
kbuf = stratix10_svc_allocate_memory ( priv - > chan , SVC_BUF_SIZE ) ;
if ( ! kbuf ) {
s10_free_buffers ( mgr ) ;
ret = - ENOMEM ;
goto init_done ;
}
priv - > svc_bufs [ i ] . buf = kbuf ;
priv - > svc_bufs [ i ] . lock = 0 ;
}
init_done :
stratix10_svc_done ( priv - > chan ) ;
return ret ;
}
/*
* s10_send_buf - send a buffer to the service layer queue
* mgr : fpga manager struct
* buf : fpga image buffer
* count : size of buf in bytes
* Returns # of bytes transferred or - ENOBUFS if the all the buffers are in use
* or if the service queue is full . Never returns 0.
*/
static int s10_send_buf ( struct fpga_manager * mgr , const char * buf , size_t count )
{
struct s10_priv * priv = mgr - > priv ;
struct device * dev = priv - > client . dev ;
void * svc_buf ;
size_t xfer_sz ;
int ret ;
uint i ;
/* get/lock a buffer that that's not being used */
for ( i = 0 ; i < NUM_SVC_BUFS ; i + + )
if ( ! test_and_set_bit_lock ( SVC_BUF_LOCK ,
& priv - > svc_bufs [ i ] . lock ) )
break ;
if ( i = = NUM_SVC_BUFS )
return - ENOBUFS ;
xfer_sz = count < SVC_BUF_SIZE ? count : SVC_BUF_SIZE ;
svc_buf = priv - > svc_bufs [ i ] . buf ;
memcpy ( svc_buf , buf , xfer_sz ) ;
ret = s10_svc_send_msg ( priv , COMMAND_RECONFIG_DATA_SUBMIT ,
svc_buf , xfer_sz ) ;
if ( ret < 0 ) {
dev_err ( dev ,
" Error while sending data to service layer (%d) " , ret ) ;
clear_bit_unlock ( SVC_BUF_LOCK , & priv - > svc_bufs [ i ] . lock ) ;
return ret ;
}
return xfer_sz ;
}
/*
* Send a FPGA image to privileged layers to write to the FPGA . When done
* sending , free all service layer buffers we allocated in write_init .
*/
static int s10_ops_write ( struct fpga_manager * mgr , const char * buf ,
size_t count )
{
struct s10_priv * priv = mgr - > priv ;
struct device * dev = priv - > client . dev ;
long wait_status ;
int sent = 0 ;
int ret = 0 ;
/*
* Loop waiting for buffers to be returned . When a buffer is returned ,
* reuse it to send more data or free if if all data has been sent .
*/
while ( count > 0 | | s10_free_buffer_count ( mgr ) ! = NUM_SVC_BUFS ) {
reinit_completion ( & priv - > status_return_completion ) ;
if ( count > 0 ) {
sent = s10_send_buf ( mgr , buf , count ) ;
if ( sent < 0 )
continue ;
count - = sent ;
buf + = sent ;
} else {
if ( s10_free_buffers ( mgr ) )
return 0 ;
ret = s10_svc_send_msg (
priv , COMMAND_RECONFIG_DATA_CLAIM ,
NULL , 0 ) ;
if ( ret < 0 )
break ;
}
/*
* If callback hasn ' t already happened , wait for buffers to be
* returned from service layer
*/
wait_status = 1 ; /* not timed out */
if ( ! priv - > status )
wait_status = wait_for_completion_interruptible_timeout (
& priv - > status_return_completion ,
S10_BUFFER_TIMEOUT ) ;
if ( test_and_clear_bit ( SVC_STATUS_RECONFIG_BUFFER_DONE ,
& priv - > status ) | |
test_and_clear_bit ( SVC_STATUS_RECONFIG_BUFFER_SUBMITTED ,
& priv - > status ) ) {
ret = 0 ;
continue ;
}
if ( test_and_clear_bit ( SVC_STATUS_RECONFIG_ERROR ,
& priv - > status ) ) {
dev_err ( dev , " ERROR - giving up - SVC_STATUS_RECONFIG_ERROR \n " ) ;
ret = - EFAULT ;
break ;
}
if ( ! wait_status ) {
dev_err ( dev , " timeout waiting for svc layer buffers \n " ) ;
ret = - ETIMEDOUT ;
break ;
}
if ( wait_status < 0 ) {
ret = wait_status ;
dev_err ( dev ,
" error (%d) waiting for svc layer buffers \n " ,
ret ) ;
break ;
}
}
if ( ! s10_free_buffers ( mgr ) )
dev_err ( dev , " %s not all buffers were freed \n " , __func__ ) ;
return ret ;
}
static int s10_ops_write_complete ( struct fpga_manager * mgr ,
struct fpga_image_info * info )
{
struct s10_priv * priv = mgr - > priv ;
struct device * dev = priv - > client . dev ;
unsigned long timeout ;
int ret ;
timeout = usecs_to_jiffies ( info - > config_complete_timeout_us ) ;
do {
reinit_completion ( & priv - > status_return_completion ) ;
ret = s10_svc_send_msg ( priv , COMMAND_RECONFIG_STATUS , NULL , 0 ) ;
if ( ret < 0 )
break ;
ret = wait_for_completion_interruptible_timeout (
& priv - > status_return_completion , timeout ) ;
if ( ! ret ) {
dev_err ( dev ,
" timeout waiting for RECONFIG_COMPLETED \n " ) ;
ret = - ETIMEDOUT ;
break ;
}
if ( ret < 0 ) {
dev_err ( dev ,
" error (%d) waiting for RECONFIG_COMPLETED \n " ,
ret ) ;
break ;
}
/* Not error or timeout, so ret is # of jiffies until timeout */
timeout = ret ;
ret = 0 ;
if ( test_and_clear_bit ( SVC_STATUS_RECONFIG_COMPLETED ,
& priv - > status ) )
break ;
if ( test_and_clear_bit ( SVC_STATUS_RECONFIG_ERROR ,
& priv - > status ) ) {
dev_err ( dev , " ERROR - giving up - SVC_STATUS_RECONFIG_ERROR \n " ) ;
ret = - EFAULT ;
break ;
}
} while ( 1 ) ;
stratix10_svc_done ( priv - > chan ) ;
return ret ;
}
static enum fpga_mgr_states s10_ops_state ( struct fpga_manager * mgr )
{
return FPGA_MGR_STATE_UNKNOWN ;
}
static const struct fpga_manager_ops s10_ops = {
. state = s10_ops_state ,
. write_init = s10_ops_write_init ,
. write = s10_ops_write ,
. write_complete = s10_ops_write_complete ,
} ;
static int s10_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct s10_priv * priv ;
struct fpga_manager * mgr ;
int ret ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > client . dev = dev ;
priv - > client . receive_cb = s10_receive_callback ;
priv - > client . priv = priv ;
priv - > chan = stratix10_svc_request_channel_byname ( & priv - > client ,
SVC_CLIENT_FPGA ) ;
if ( IS_ERR ( priv - > chan ) ) {
dev_err ( dev , " couldn't get service channel (%s) \n " ,
SVC_CLIENT_FPGA ) ;
return PTR_ERR ( priv - > chan ) ;
}
init_completion ( & priv - > status_return_completion ) ;
mgr = fpga_mgr_create ( dev , " Stratix10 SOC FPGA Manager " ,
& s10_ops , priv ) ;
if ( ! mgr ) {
dev_err ( dev , " unable to create FPGA manager \n " ) ;
ret = - ENOMEM ;
goto probe_err ;
}
ret = fpga_mgr_register ( mgr ) ;
if ( ret ) {
dev_err ( dev , " unable to register FPGA manager \n " ) ;
fpga_mgr_free ( mgr ) ;
goto probe_err ;
}
platform_set_drvdata ( pdev , mgr ) ;
return ret ;
probe_err :
stratix10_svc_free_channel ( priv - > chan ) ;
return ret ;
}
static int s10_remove ( struct platform_device * pdev )
{
struct fpga_manager * mgr = platform_get_drvdata ( pdev ) ;
struct s10_priv * priv = mgr - > priv ;
fpga_mgr_unregister ( mgr ) ;
stratix10_svc_free_channel ( priv - > chan ) ;
return 0 ;
}
static const struct of_device_id s10_of_match [ ] = {
{ . compatible = " intel,stratix10-soc-fpga-mgr " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , s10_of_match ) ;
static struct platform_driver s10_driver = {
. probe = s10_probe ,
. remove = s10_remove ,
. driver = {
. name = " Stratix10 SoC FPGA manager " ,
. of_match_table = of_match_ptr ( s10_of_match ) ,
} ,
} ;
static int __init s10_init ( void )
{
struct device_node * fw_np ;
struct device_node * np ;
int ret ;
fw_np = of_find_node_by_name ( NULL , " svc " ) ;
if ( ! fw_np )
return - ENODEV ;
np = of_find_matching_node ( fw_np , s10_of_match ) ;
2019-01-26 19:38:29 +03:00
if ( ! np )
2018-11-13 21:14:04 +03:00
return - ENODEV ;
of_node_put ( np ) ;
ret = of_platform_populate ( fw_np , s10_of_match , NULL , NULL ) ;
if ( ret )
return ret ;
return platform_driver_register ( & s10_driver ) ;
}
static void __exit s10_exit ( void )
{
return platform_driver_unregister ( & s10_driver ) ;
}
module_init ( s10_init ) ;
module_exit ( s10_exit ) ;
MODULE_AUTHOR ( " Alan Tull <atull@kernel.org> " ) ;
MODULE_DESCRIPTION ( " Intel Stratix 10 SOC FPGA Manager " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;