2019-05-29 17:17:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-06-14 18:36:35 +03:00
/*
* FPGA Manager Driver for Altera Arria / Cyclone / Stratix CvP
*
* Copyright ( C ) 2017 DENX Software Engineering
*
* Anatolij Gustschin < agust @ denx . de >
*
* Manage Altera FPGA firmware using PCIe CvP .
* Firmware must be in binary " rbf " format .
*/
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/fpga/fpga-mgr.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/sizes.h>
# define CVP_BAR 0 /* BAR used for data transfer in memory mode */
# define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */
# define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */
/* Vendor Specific Extended Capability Registers */
2019-08-19 23:48:06 +03:00
# define VSE_PCIE_EXT_CAP_ID 0x0
2017-06-14 18:36:35 +03:00
# define VSE_PCIE_EXT_CAP_ID_VAL 0x000b /* 16bit */
2019-08-19 23:48:06 +03:00
# define VSE_CVP_STATUS 0x1c /* 32bit */
2017-06-14 18:36:35 +03:00
# define VSE_CVP_STATUS_CFG_RDY BIT(18) /* CVP_CONFIG_READY */
# define VSE_CVP_STATUS_CFG_ERR BIT(19) /* CVP_CONFIG_ERROR */
# define VSE_CVP_STATUS_CVP_EN BIT(20) /* ctrl block is enabling CVP */
# define VSE_CVP_STATUS_USERMODE BIT(21) /* USERMODE */
# define VSE_CVP_STATUS_CFG_DONE BIT(23) /* CVP_CONFIG_DONE */
# define VSE_CVP_STATUS_PLD_CLK_IN_USE BIT(24) /* PLD_CLK_IN_USE */
2019-08-19 23:48:06 +03:00
# define VSE_CVP_MODE_CTRL 0x20 /* 32bit */
2017-06-14 18:36:35 +03:00
# define VSE_CVP_MODE_CTRL_CVP_MODE BIT(0) /* CVP (1) or normal mode (0) */
# define VSE_CVP_MODE_CTRL_HIP_CLK_SEL BIT(1) /* PMA (1) or fabric clock (0) */
# define VSE_CVP_MODE_CTRL_NUMCLKS_OFF 8 /* NUMCLKS bits offset */
# define VSE_CVP_MODE_CTRL_NUMCLKS_MASK GENMASK(15, 8)
2019-08-19 23:48:06 +03:00
# define VSE_CVP_DATA 0x28 /* 32bit */
# define VSE_CVP_PROG_CTRL 0x2c /* 32bit */
2017-06-14 18:36:35 +03:00
# define VSE_CVP_PROG_CTRL_CONFIG BIT(0)
# define VSE_CVP_PROG_CTRL_START_XFER BIT(1)
2019-08-19 23:48:08 +03:00
# define VSE_CVP_PROG_CTRL_MASK GENMASK(1, 0)
2017-06-14 18:36:35 +03:00
2019-08-19 23:48:06 +03:00
# define VSE_UNCOR_ERR_STATUS 0x34 /* 32bit */
2017-06-14 18:36:35 +03:00
# define VSE_UNCOR_ERR_CVP_CFG_ERR BIT(5) /* CVP_CONFIG_ERROR_LATCHED */
2019-08-19 23:48:08 +03:00
# define V1_VSEC_OFFSET 0x200 /* Vendor Specific Offset V1 */
/* V2 Defines */
# define VSE_CVP_TX_CREDITS 0x49 /* 8bit */
# define V2_CREDIT_TIMEOUT_US 20000
# define V2_CHECK_CREDIT_US 10
# define V2_POLL_TIMEOUT_US 1000000
# define V2_USER_TIMEOUT_US 500000
# define V1_POLL_TIMEOUT_US 10
2017-06-14 18:36:35 +03:00
# define DRV_NAME "altera-cvp"
# define ALTERA_CVP_MGR_NAME "Altera CvP FPGA Manager"
2019-08-19 23:48:08 +03:00
/* Write block sizes */
# define ALTERA_CVP_V1_SIZE 4
# define ALTERA_CVP_V2_SIZE 4096
2017-06-14 18:36:35 +03:00
/* Optional CvP config error status check for debugging */
static bool altera_cvp_chkcfg ;
2019-08-19 23:48:08 +03:00
struct cvp_priv ;
2017-06-14 18:36:35 +03:00
struct altera_cvp_conf {
struct fpga_manager * mgr ;
struct pci_dev * pci_dev ;
void __iomem * map ;
2018-11-08 04:13:12 +03:00
void ( * write_data ) ( struct altera_cvp_conf * conf ,
u32 data ) ;
2017-06-14 18:36:35 +03:00
char mgr_name [ 64 ] ;
u8 numclks ;
2019-08-19 23:48:08 +03:00
u32 sent_packets ;
2019-08-19 23:48:06 +03:00
u32 vsec_offset ;
2019-08-19 23:48:08 +03:00
const struct cvp_priv * priv ;
2017-06-14 18:36:35 +03:00
} ;
2019-08-19 23:48:08 +03:00
struct cvp_priv {
void ( * switch_clk ) ( struct altera_cvp_conf * conf ) ;
int ( * clear_state ) ( struct altera_cvp_conf * conf ) ;
int ( * wait_credit ) ( struct fpga_manager * mgr , u32 blocks ) ;
size_t block_size ;
int poll_time_us ;
int user_time_us ;
} ;
static int altera_read_config_byte ( struct altera_cvp_conf * conf ,
int where , u8 * val )
{
return pci_read_config_byte ( conf - > pci_dev , conf - > vsec_offset + where ,
val ) ;
}
2019-08-19 23:48:06 +03:00
static int altera_read_config_dword ( struct altera_cvp_conf * conf ,
int where , u32 * val )
{
return pci_read_config_dword ( conf - > pci_dev , conf - > vsec_offset + where ,
val ) ;
}
static int altera_write_config_dword ( struct altera_cvp_conf * conf ,
int where , u32 val )
{
return pci_write_config_dword ( conf - > pci_dev , conf - > vsec_offset + where ,
val ) ;
}
2017-06-14 18:36:35 +03:00
static enum fpga_mgr_states altera_cvp_state ( struct fpga_manager * mgr )
{
struct altera_cvp_conf * conf = mgr - > priv ;
u32 status ;
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_STATUS , & status ) ;
2017-06-14 18:36:35 +03:00
if ( status & VSE_CVP_STATUS_CFG_DONE )
return FPGA_MGR_STATE_OPERATING ;
if ( status & VSE_CVP_STATUS_CVP_EN )
return FPGA_MGR_STATE_POWER_UP ;
return FPGA_MGR_STATE_UNKNOWN ;
}
static void altera_cvp_write_data_iomem ( struct altera_cvp_conf * conf , u32 val )
{
writel ( val , conf - > map ) ;
}
static void altera_cvp_write_data_config ( struct altera_cvp_conf * conf , u32 val )
{
2019-08-19 23:48:06 +03:00
pci_write_config_dword ( conf - > pci_dev , conf - > vsec_offset + VSE_CVP_DATA ,
val ) ;
2017-06-14 18:36:35 +03:00
}
/* switches between CvP clock and internal clock */
static void altera_cvp_dummy_write ( struct altera_cvp_conf * conf )
{
unsigned int i ;
u32 val ;
/* set 1 CVP clock cycle for every CVP Data Register Write */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_MODE_CTRL , & val ) ;
2017-06-14 18:36:35 +03:00
val & = ~ VSE_CVP_MODE_CTRL_NUMCLKS_MASK ;
val | = 1 < < VSE_CVP_MODE_CTRL_NUMCLKS_OFF ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_MODE_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
for ( i = 0 ; i < CVP_DUMMY_WR ; i + + )
conf - > write_data ( conf , 0 ) ; /* dummy data, could be any value */
}
static int altera_cvp_wait_status ( struct altera_cvp_conf * conf , u32 status_mask ,
u32 status_val , int timeout_us )
{
unsigned int retries ;
u32 val ;
retries = timeout_us / 10 ;
if ( timeout_us % 10 )
retries + + ;
do {
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_STATUS , & val ) ;
2017-06-14 18:36:35 +03:00
if ( ( val & status_mask ) = = status_val )
return 0 ;
/* use small usleep value to re-check and break early */
usleep_range ( 10 , 11 ) ;
} while ( - - retries ) ;
return - ETIMEDOUT ;
}
2019-08-19 23:48:07 +03:00
static int altera_cvp_chk_error ( struct fpga_manager * mgr , size_t bytes )
{
struct altera_cvp_conf * conf = mgr - > priv ;
u32 val ;
int ret ;
/* STEP 10 (optional) - check CVP_CONFIG_ERROR flag */
ret = altera_read_config_dword ( conf , VSE_CVP_STATUS , & val ) ;
if ( ret | | ( val & VSE_CVP_STATUS_CFG_ERR ) ) {
dev_err ( & mgr - > dev , " CVP_CONFIG_ERROR after %zu bytes! \n " ,
bytes ) ;
return - EPROTO ;
}
return 0 ;
}
2019-08-19 23:48:08 +03:00
/*
* CvP Version2 Functions
* Recent Intel FPGAs use a credit mechanism to throttle incoming
* bitstreams and a different method of clearing the state .
*/
static int altera_cvp_v2_clear_state ( struct altera_cvp_conf * conf )
{
u32 val ;
int ret ;
/* Clear the START_XFER and CVP_CONFIG bits */
ret = altera_read_config_dword ( conf , VSE_CVP_PROG_CTRL , & val ) ;
if ( ret ) {
dev_err ( & conf - > pci_dev - > dev ,
" Error reading CVP Program Control Register \n " ) ;
return ret ;
}
val & = ~ VSE_CVP_PROG_CTRL_MASK ;
ret = altera_write_config_dword ( conf , VSE_CVP_PROG_CTRL , val ) ;
if ( ret ) {
dev_err ( & conf - > pci_dev - > dev ,
" Error writing CVP Program Control Register \n " ) ;
return ret ;
}
return altera_cvp_wait_status ( conf , VSE_CVP_STATUS_CFG_RDY , 0 ,
conf - > priv - > poll_time_us ) ;
}
static int altera_cvp_v2_wait_for_credit ( struct fpga_manager * mgr ,
u32 blocks )
{
u32 timeout = V2_CREDIT_TIMEOUT_US / V2_CHECK_CREDIT_US ;
struct altera_cvp_conf * conf = mgr - > priv ;
int ret ;
u8 val ;
do {
ret = altera_read_config_byte ( conf , VSE_CVP_TX_CREDITS , & val ) ;
if ( ret ) {
dev_err ( & conf - > pci_dev - > dev ,
" Error reading CVP Credit Register \n " ) ;
return ret ;
}
/* Return if there is space in FIFO */
if ( val - ( u8 ) conf - > sent_packets )
return 0 ;
ret = altera_cvp_chk_error ( mgr , blocks * ALTERA_CVP_V2_SIZE ) ;
if ( ret ) {
dev_err ( & conf - > pci_dev - > dev ,
" CE Bit error credit reg[0x%x]:sent[0x%x] \n " ,
val , conf - > sent_packets ) ;
return - EAGAIN ;
}
/* Limit the check credit byte traffic */
usleep_range ( V2_CHECK_CREDIT_US , V2_CHECK_CREDIT_US + 1 ) ;
} while ( timeout - - ) ;
dev_err ( & conf - > pci_dev - > dev , " Timeout waiting for credit \n " ) ;
return - ETIMEDOUT ;
}
2019-08-19 23:48:07 +03:00
static int altera_cvp_send_block ( struct altera_cvp_conf * conf ,
const u32 * data , size_t len )
{
u32 mask , words = len / sizeof ( u32 ) ;
int i , remainder ;
for ( i = 0 ; i < words ; i + + )
conf - > write_data ( conf , * data + + ) ;
/* write up to 3 trailing bytes, if any */
remainder = len % sizeof ( u32 ) ;
if ( remainder ) {
mask = BIT ( remainder * 8 ) - 1 ;
if ( mask )
conf - > write_data ( conf , * data & mask ) ;
}
return 0 ;
}
2017-06-14 18:36:35 +03:00
static int altera_cvp_teardown ( struct fpga_manager * mgr ,
struct fpga_image_info * info )
{
struct altera_cvp_conf * conf = mgr - > priv ;
int ret ;
u32 val ;
/* STEP 12 - reset START_XFER bit */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_PROG_CTRL , & val ) ;
2017-06-14 18:36:35 +03:00
val & = ~ VSE_CVP_PROG_CTRL_START_XFER ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_PROG_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
/* STEP 13 - reset CVP_CONFIG bit */
val & = ~ VSE_CVP_PROG_CTRL_CONFIG ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_PROG_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
/*
* STEP 14
* - set CVP_NUMCLKS to 1 and then issue CVP_DUMMY_WR dummy
* writes to the HIP
*/
2019-08-19 23:48:08 +03:00
if ( conf - > priv - > switch_clk )
conf - > priv - > switch_clk ( conf ) ;
2017-06-14 18:36:35 +03:00
/* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */
2019-08-19 23:48:08 +03:00
ret = altera_cvp_wait_status ( conf , VSE_CVP_STATUS_CFG_RDY , 0 ,
conf - > priv - > poll_time_us ) ;
2017-06-14 18:36:35 +03:00
if ( ret )
dev_err ( & mgr - > dev , " CFG_RDY == 0 timeout \n " ) ;
return ret ;
}
static int altera_cvp_write_init ( struct fpga_manager * mgr ,
struct fpga_image_info * info ,
const char * buf , size_t count )
{
struct altera_cvp_conf * conf = mgr - > priv ;
u32 iflags , val ;
int ret ;
iflags = info ? info - > flags : 0 ;
if ( iflags & FPGA_MGR_PARTIAL_RECONFIG ) {
dev_err ( & mgr - > dev , " Partial reconfiguration not supported. \n " ) ;
return - EINVAL ;
}
/* Determine allowed clock to data ratio */
if ( iflags & FPGA_MGR_COMPRESSED_BITSTREAM )
conf - > numclks = 8 ; /* ratio for all compressed images */
else if ( iflags & FPGA_MGR_ENCRYPTED_BITSTREAM )
conf - > numclks = 4 ; /* for uncompressed and encrypted images */
else
conf - > numclks = 1 ; /* for uncompressed and unencrypted images */
/* STEP 1 - read CVP status and check CVP_EN flag */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_STATUS , & val ) ;
2017-06-14 18:36:35 +03:00
if ( ! ( val & VSE_CVP_STATUS_CVP_EN ) ) {
dev_err ( & mgr - > dev , " CVP mode off: 0x%04x \n " , val ) ;
return - ENODEV ;
}
if ( val & VSE_CVP_STATUS_CFG_RDY ) {
dev_warn ( & mgr - > dev , " CvP already started, teardown first \n " ) ;
ret = altera_cvp_teardown ( mgr , info ) ;
if ( ret )
return ret ;
}
/*
* STEP 2
* - set HIP_CLK_SEL and CVP_MODE ( must be set in the order mentioned )
*/
/* switch from fabric to PMA clock */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_MODE_CTRL , & val ) ;
2017-06-14 18:36:35 +03:00
val | = VSE_CVP_MODE_CTRL_HIP_CLK_SEL ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_MODE_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
/* set CVP mode */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_MODE_CTRL , & val ) ;
2017-06-14 18:36:35 +03:00
val | = VSE_CVP_MODE_CTRL_CVP_MODE ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_MODE_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
/*
* STEP 3
* - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP
*/
2019-08-19 23:48:08 +03:00
if ( conf - > priv - > switch_clk )
conf - > priv - > switch_clk ( conf ) ;
if ( conf - > priv - > clear_state ) {
ret = conf - > priv - > clear_state ( conf ) ;
if ( ret ) {
dev_err ( & mgr - > dev , " Problem clearing out state \n " ) ;
return ret ;
}
}
conf - > sent_packets = 0 ;
2017-06-14 18:36:35 +03:00
/* STEP 4 - set CVP_CONFIG bit */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_PROG_CTRL , & val ) ;
2017-06-14 18:36:35 +03:00
/* request control block to begin transfer using CVP */
val | = VSE_CVP_PROG_CTRL_CONFIG ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_PROG_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
2019-08-19 23:48:08 +03:00
/* STEP 5 - poll CVP_CONFIG READY for 1 with timeout */
2017-06-14 18:36:35 +03:00
ret = altera_cvp_wait_status ( conf , VSE_CVP_STATUS_CFG_RDY ,
2019-08-19 23:48:08 +03:00
VSE_CVP_STATUS_CFG_RDY ,
conf - > priv - > poll_time_us ) ;
2017-06-14 18:36:35 +03:00
if ( ret ) {
dev_warn ( & mgr - > dev , " CFG_RDY == 1 timeout \n " ) ;
return ret ;
}
/*
* STEP 6
* - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP
*/
2019-08-19 23:48:08 +03:00
if ( conf - > priv - > switch_clk )
conf - > priv - > switch_clk ( conf ) ;
if ( altera_cvp_chkcfg ) {
ret = altera_cvp_chk_error ( mgr , 0 ) ;
if ( ret ) {
dev_warn ( & mgr - > dev , " CFG_RDY == 1 timeout \n " ) ;
return ret ;
}
}
2017-06-14 18:36:35 +03:00
/* STEP 7 - set START_XFER */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_PROG_CTRL , & val ) ;
2017-06-14 18:36:35 +03:00
val | = VSE_CVP_PROG_CTRL_START_XFER ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_PROG_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
/* STEP 8 - start transfer (set CVP_NUMCLKS for bitstream) */
2019-08-19 23:48:08 +03:00
if ( conf - > priv - > switch_clk ) {
altera_read_config_dword ( conf , VSE_CVP_MODE_CTRL , & val ) ;
val & = ~ VSE_CVP_MODE_CTRL_NUMCLKS_MASK ;
val | = conf - > numclks < < VSE_CVP_MODE_CTRL_NUMCLKS_OFF ;
altera_write_config_dword ( conf , VSE_CVP_MODE_CTRL , val ) ;
}
2017-06-14 18:36:35 +03:00
return 0 ;
}
static int altera_cvp_write ( struct fpga_manager * mgr , const char * buf ,
size_t count )
{
struct altera_cvp_conf * conf = mgr - > priv ;
2019-08-19 23:48:07 +03:00
size_t done , remaining , len ;
2017-06-14 18:36:35 +03:00
const u32 * data ;
int status = 0 ;
/* STEP 9 - write 32-bit data from RBF file to CVP data register */
data = ( u32 * ) buf ;
remaining = count ;
done = 0 ;
2019-08-19 23:48:07 +03:00
while ( remaining ) {
2019-08-19 23:48:08 +03:00
/* Use credit throttling if available */
if ( conf - > priv - > wait_credit ) {
status = conf - > priv - > wait_credit ( mgr , done ) ;
if ( status ) {
dev_err ( & conf - > pci_dev - > dev ,
" Wait Credit ERR: 0x%x \n " , status ) ;
return status ;
}
}
len = min ( conf - > priv - > block_size , remaining ) ;
2019-08-19 23:48:07 +03:00
altera_cvp_send_block ( conf , data , len ) ;
2019-08-19 23:48:08 +03:00
data + = len / sizeof ( u32 ) ;
2019-08-19 23:48:07 +03:00
done + = len ;
remaining - = len ;
2019-08-19 23:48:08 +03:00
conf - > sent_packets + + ;
2017-06-14 18:36:35 +03:00
/*
* STEP 10 ( optional ) and STEP 11
* - check error flag
* - loop until data transfer completed
* Config images can be huge ( more than 40 MiB ) , so
* only check after a new 4 k data block has been written .
* This reduces the number of checks and speeds up the
* configuration process .
*/
if ( altera_cvp_chkcfg & & ! ( done % SZ_4K ) ) {
status = altera_cvp_chk_error ( mgr , done ) ;
if ( status < 0 )
return status ;
}
}
if ( altera_cvp_chkcfg )
status = altera_cvp_chk_error ( mgr , count ) ;
return status ;
}
static int altera_cvp_write_complete ( struct fpga_manager * mgr ,
struct fpga_image_info * info )
{
struct altera_cvp_conf * conf = mgr - > priv ;
2019-08-19 23:48:06 +03:00
u32 mask , val ;
2017-06-14 18:36:35 +03:00
int ret ;
ret = altera_cvp_teardown ( mgr , info ) ;
if ( ret )
return ret ;
/* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_UNCOR_ERR_STATUS , & val ) ;
2017-06-14 18:36:35 +03:00
if ( val & VSE_UNCOR_ERR_CVP_CFG_ERR ) {
dev_err ( & mgr - > dev , " detected CVP_CONFIG_ERROR_LATCHED! \n " ) ;
return - EPROTO ;
}
/* STEP 17 - reset CVP_MODE and HIP_CLK_SEL bit */
2019-08-19 23:48:06 +03:00
altera_read_config_dword ( conf , VSE_CVP_MODE_CTRL , & val ) ;
2017-06-14 18:36:35 +03:00
val & = ~ VSE_CVP_MODE_CTRL_HIP_CLK_SEL ;
val & = ~ VSE_CVP_MODE_CTRL_CVP_MODE ;
2019-08-19 23:48:06 +03:00
altera_write_config_dword ( conf , VSE_CVP_MODE_CTRL , val ) ;
2017-06-14 18:36:35 +03:00
/* STEP 18 - poll PLD_CLK_IN_USE and USER_MODE bits */
mask = VSE_CVP_STATUS_PLD_CLK_IN_USE | VSE_CVP_STATUS_USERMODE ;
2019-08-19 23:48:08 +03:00
ret = altera_cvp_wait_status ( conf , mask , mask ,
conf - > priv - > user_time_us ) ;
2017-06-14 18:36:35 +03:00
if ( ret )
dev_err ( & mgr - > dev , " PLD_CLK_IN_USE|USERMODE timeout \n " ) ;
return ret ;
}
static const struct fpga_manager_ops altera_cvp_ops = {
. state = altera_cvp_state ,
. write_init = altera_cvp_write_init ,
. write = altera_cvp_write ,
. write_complete = altera_cvp_write_complete ,
} ;
2019-08-19 23:48:08 +03:00
static const struct cvp_priv cvp_priv_v1 = {
. switch_clk = altera_cvp_dummy_write ,
. block_size = ALTERA_CVP_V1_SIZE ,
. poll_time_us = V1_POLL_TIMEOUT_US ,
. user_time_us = TIMEOUT_US ,
} ;
static const struct cvp_priv cvp_priv_v2 = {
. clear_state = altera_cvp_v2_clear_state ,
. wait_credit = altera_cvp_v2_wait_for_credit ,
. block_size = ALTERA_CVP_V2_SIZE ,
. poll_time_us = V2_POLL_TIMEOUT_US ,
. user_time_us = V2_USER_TIMEOUT_US ,
} ;
2017-09-18 13:17:36 +03:00
static ssize_t chkcfg_show ( struct device_driver * dev , char * buf )
2017-06-14 18:36:35 +03:00
{
return snprintf ( buf , 3 , " %d \n " , altera_cvp_chkcfg ) ;
}
2017-09-18 13:17:36 +03:00
static ssize_t chkcfg_store ( struct device_driver * drv , const char * buf ,
2017-06-14 18:36:35 +03:00
size_t count )
{
int ret ;
ret = kstrtobool ( buf , & altera_cvp_chkcfg ) ;
if ( ret )
return ret ;
return count ;
}
2017-09-18 13:17:36 +03:00
static DRIVER_ATTR_RW ( chkcfg ) ;
2017-06-14 18:36:35 +03:00
static int altera_cvp_probe ( struct pci_dev * pdev ,
const struct pci_device_id * dev_id ) ;
static void altera_cvp_remove ( struct pci_dev * pdev ) ;
static struct pci_device_id altera_cvp_id_tbl [ ] = {
{ PCI_VDEVICE ( ALTERA , PCI_ANY_ID ) } ,
{ }
} ;
MODULE_DEVICE_TABLE ( pci , altera_cvp_id_tbl ) ;
static struct pci_driver altera_cvp_driver = {
. name = DRV_NAME ,
. id_table = altera_cvp_id_tbl ,
. probe = altera_cvp_probe ,
. remove = altera_cvp_remove ,
} ;
static int altera_cvp_probe ( struct pci_dev * pdev ,
const struct pci_device_id * dev_id )
{
struct altera_cvp_conf * conf ;
2018-05-17 02:49:55 +03:00
struct fpga_manager * mgr ;
2019-08-19 23:48:06 +03:00
int ret , offset ;
2017-06-14 18:36:35 +03:00
u16 cmd , val ;
2018-11-07 20:51:47 +03:00
u32 regval ;
2019-08-19 23:48:06 +03:00
/* Discover the Vendor Specific Offset for this device */
offset = pci_find_next_ext_capability ( pdev , 0 , PCI_EXT_CAP_ID_VNDR ) ;
if ( ! offset ) {
dev_err ( & pdev - > dev , " No Vendor Specific Offset. \n " ) ;
return - ENODEV ;
}
2017-06-14 18:36:35 +03:00
/*
* First check if this is the expected FPGA device . PCI config
* space access works without enabling the PCI device , memory
* space access is enabled further down .
*/
2019-08-19 23:48:06 +03:00
pci_read_config_word ( pdev , offset + VSE_PCIE_EXT_CAP_ID , & val ) ;
2017-06-14 18:36:35 +03:00
if ( val ! = VSE_PCIE_EXT_CAP_ID_VAL ) {
dev_err ( & pdev - > dev , " Wrong EXT_CAP_ID value 0x%x \n " , val ) ;
return - ENODEV ;
}
2019-08-19 23:48:06 +03:00
pci_read_config_dword ( pdev , offset + VSE_CVP_STATUS , & regval ) ;
2018-11-07 20:51:47 +03:00
if ( ! ( regval & VSE_CVP_STATUS_CVP_EN ) ) {
dev_err ( & pdev - > dev ,
" CVP is disabled for this device: CVP_STATUS Reg 0x%x \n " ,
regval ) ;
return - ENODEV ;
}
2017-06-14 18:36:35 +03:00
conf = devm_kzalloc ( & pdev - > dev , sizeof ( * conf ) , GFP_KERNEL ) ;
if ( ! conf )
return - ENOMEM ;
2019-08-19 23:48:06 +03:00
conf - > vsec_offset = offset ;
2017-06-14 18:36:35 +03:00
/*
* Enable memory BAR access . We cannot use pci_enable_device ( ) here
* because it will make the driver unusable with FPGA devices that
* have additional big IOMEM resources ( e . g . 4 GiB BARs ) on 32 - bit
* platform . Such BARs will not have an assigned address range and
* pci_enable_device ( ) will fail , complaining about not claimed BAR ,
* even if the concerned BAR is not needed for FPGA configuration
* at all . Thus , enable the device via PCI config space command .
*/
pci_read_config_word ( pdev , PCI_COMMAND , & cmd ) ;
if ( ! ( cmd & PCI_COMMAND_MEMORY ) ) {
cmd | = PCI_COMMAND_MEMORY ;
pci_write_config_word ( pdev , PCI_COMMAND , cmd ) ;
}
ret = pci_request_region ( pdev , CVP_BAR , " CVP " ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Requesting CVP BAR region failed \n " ) ;
goto err_disable ;
}
conf - > pci_dev = pdev ;
conf - > write_data = altera_cvp_write_data_iomem ;
2019-08-19 23:48:08 +03:00
if ( conf - > vsec_offset = = V1_VSEC_OFFSET )
conf - > priv = & cvp_priv_v1 ;
else
conf - > priv = & cvp_priv_v2 ;
2017-06-14 18:36:35 +03:00
conf - > map = pci_iomap ( pdev , CVP_BAR , 0 ) ;
if ( ! conf - > map ) {
dev_warn ( & pdev - > dev , " Mapping CVP BAR failed \n " ) ;
conf - > write_data = altera_cvp_write_data_config ;
}
snprintf ( conf - > mgr_name , sizeof ( conf - > mgr_name ) , " %s @%s " ,
ALTERA_CVP_MGR_NAME , pci_name ( pdev ) ) ;
2018-10-16 01:20:01 +03:00
mgr = devm_fpga_mgr_create ( & pdev - > dev , conf - > mgr_name ,
& altera_cvp_ops , conf ) ;
2018-06-28 04:56:18 +03:00
if ( ! mgr ) {
ret = - ENOMEM ;
goto err_unmap ;
}
2018-05-17 02:49:55 +03:00
pci_set_drvdata ( pdev , mgr ) ;
ret = fpga_mgr_register ( mgr ) ;
2018-10-16 01:20:01 +03:00
if ( ret )
2017-06-14 18:36:35 +03:00
goto err_unmap ;
return 0 ;
err_unmap :
2018-11-07 20:51:45 +03:00
if ( conf - > map )
pci_iounmap ( pdev , conf - > map ) ;
2017-06-14 18:36:35 +03:00
pci_release_region ( pdev , CVP_BAR ) ;
err_disable :
cmd & = ~ PCI_COMMAND_MEMORY ;
pci_write_config_word ( pdev , PCI_COMMAND , cmd ) ;
return ret ;
}
static void altera_cvp_remove ( struct pci_dev * pdev )
{
struct fpga_manager * mgr = pci_get_drvdata ( pdev ) ;
struct altera_cvp_conf * conf = mgr - > priv ;
u16 cmd ;
2018-05-17 02:49:55 +03:00
fpga_mgr_unregister ( mgr ) ;
2018-11-07 20:51:45 +03:00
if ( conf - > map )
pci_iounmap ( pdev , conf - > map ) ;
2017-06-14 18:36:35 +03:00
pci_release_region ( pdev , CVP_BAR ) ;
pci_read_config_word ( pdev , PCI_COMMAND , & cmd ) ;
cmd & = ~ PCI_COMMAND_MEMORY ;
pci_write_config_word ( pdev , PCI_COMMAND , cmd ) ;
}
2018-11-26 20:35:27 +03:00
static int __init altera_cvp_init ( void )
{
int ret ;
ret = pci_register_driver ( & altera_cvp_driver ) ;
if ( ret )
return ret ;
ret = driver_create_file ( & altera_cvp_driver . driver ,
& driver_attr_chkcfg ) ;
if ( ret )
pr_warn ( " Can't create sysfs chkcfg file \n " ) ;
return 0 ;
}
static void __exit altera_cvp_exit ( void )
{
driver_remove_file ( & altera_cvp_driver . driver , & driver_attr_chkcfg ) ;
pci_unregister_driver ( & altera_cvp_driver ) ;
}
module_init ( altera_cvp_init ) ;
module_exit ( altera_cvp_exit ) ;
2017-06-14 18:36:35 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Anatolij Gustschin <agust@denx.de> " ) ;
MODULE_DESCRIPTION ( " Module to load Altera FPGA over CvP " ) ;