2018-06-30 03:53:30 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for FPGA Accelerated Function Unit ( AFU )
*
* Copyright ( C ) 2017 - 2018 Intel Corporation , Inc .
*
* Authors :
* Wu Hao < hao . wu @ intel . com >
* Xiao Guangrong < guangrong . xiao @ linux . intel . com >
* Joseph Grecco < joe . grecco @ intel . com >
* Enno Luebbers < enno . luebbers @ intel . com >
* Tim Whisonant < tim . whisonant @ intel . com >
* Ananda Ravuri < ananda . ravuri @ intel . com >
* Henry Mitchel < henry . mitchel @ intel . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
2018-06-30 03:53:34 +03:00
# include <linux/uaccess.h>
2018-06-30 03:53:32 +03:00
# include <linux/fpga-dfl.h>
2018-06-30 03:53:30 +03:00
2018-06-30 03:53:34 +03:00
# include "dfl-afu.h"
2018-06-30 03:53:30 +03:00
2021-03-24 01:46:50 +03:00
# define RST_POLL_INVL 10 /* us */
# define RST_POLL_TIMEOUT 1000 /* us */
2018-06-30 03:53:31 +03:00
/**
2019-08-12 05:50:00 +03:00
* __afu_port_enable - enable a port by clear reset
2018-06-30 03:53:31 +03:00
* @ pdev : port platform device .
*
* Enable Port by clear the port soft reset bit , which is set by default .
2018-06-30 03:53:34 +03:00
* The AFU is unable to respond to any MMIO access while in reset .
2019-08-12 05:50:00 +03:00
* __afu_port_enable function should only be used after __afu_port_disable
* function .
*
* The caller needs to hold lock for protection .
2018-06-30 03:53:31 +03:00
*/
2021-03-24 01:46:50 +03:00
int __afu_port_enable ( struct platform_device * pdev )
2018-06-30 03:53:31 +03:00
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
void __iomem * base ;
u64 v ;
WARN_ON ( ! pdata - > disable_count ) ;
if ( - - pdata - > disable_count ! = 0 )
2021-03-24 01:46:50 +03:00
return 0 ;
2018-06-30 03:53:31 +03:00
base = dfl_get_feature_ioaddr_by_id ( & pdev - > dev , PORT_FEATURE_ID_HEADER ) ;
/* Clear port soft reset */
v = readq ( base + PORT_HDR_CTRL ) ;
v & = ~ PORT_CTRL_SFTRST ;
writeq ( v , base + PORT_HDR_CTRL ) ;
2021-03-24 01:46:50 +03:00
/*
* HW clears the ack bit to indicate that the port is fully out
* of reset .
*/
if ( readq_poll_timeout ( base + PORT_HDR_CTRL , v ,
! ( v & PORT_CTRL_SFTRST_ACK ) ,
RST_POLL_INVL , RST_POLL_TIMEOUT ) ) {
dev_err ( & pdev - > dev , " timeout, failure to enable device \n " ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
2018-06-30 03:53:31 +03:00
/**
2019-08-12 05:50:00 +03:00
* __afu_port_disable - disable a port by hold reset
2018-06-30 03:53:31 +03:00
* @ pdev : port platform device .
*
2019-08-12 05:50:00 +03:00
* Disable Port by setting the port soft reset bit , it puts the port into reset .
*
* The caller needs to hold lock for protection .
2018-06-30 03:53:31 +03:00
*/
2019-08-12 05:50:00 +03:00
int __afu_port_disable ( struct platform_device * pdev )
2018-06-30 03:53:31 +03:00
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
void __iomem * base ;
u64 v ;
if ( pdata - > disable_count + + ! = 0 )
return 0 ;
base = dfl_get_feature_ioaddr_by_id ( & pdev - > dev , PORT_FEATURE_ID_HEADER ) ;
/* Set port soft reset */
v = readq ( base + PORT_HDR_CTRL ) ;
v | = PORT_CTRL_SFTRST ;
writeq ( v , base + PORT_HDR_CTRL ) ;
/*
* HW sets ack bit to 1 when all outstanding requests have been drained
* on this port and minimum soft reset pulse width has elapsed .
* Driver polls port_soft_reset_ack to determine if reset done by HW .
*/
2020-07-13 09:10:03 +03:00
if ( readq_poll_timeout ( base + PORT_HDR_CTRL , v ,
v & PORT_CTRL_SFTRST_ACK ,
2018-06-30 03:53:31 +03:00
RST_POLL_INVL , RST_POLL_TIMEOUT ) ) {
2021-03-24 01:46:50 +03:00
dev_err ( & pdev - > dev , " timeout, failure to disable device \n " ) ;
2018-06-30 03:53:31 +03:00
return - ETIMEDOUT ;
}
return 0 ;
}
2018-06-30 03:53:32 +03:00
/*
* This function resets the FPGA Port and its accelerator ( AFU ) by function
* __port_disable and __port_enable ( set port soft reset bit and then clear
* it ) . Userspace can do Port reset at any time , e . g . during DMA or Partial
* Reconfiguration . But it should never cause any system level issue , only
* functional failure ( e . g . DMA or PR operation failure ) and be recoverable
* from the failure .
*
* Note : the accelerator ( AFU ) is not accessible when its port is in reset
* ( disabled ) . Any attempts on MMIO access to AFU while in reset , will
* result errors reported via port error reporting sub feature ( if present ) .
*/
static int __port_reset ( struct platform_device * pdev )
{
int ret ;
2019-08-12 05:50:00 +03:00
ret = __afu_port_disable ( pdev ) ;
2021-03-24 01:46:50 +03:00
if ( ret )
return ret ;
2018-06-30 03:53:32 +03:00
2021-03-24 01:46:50 +03:00
return __afu_port_enable ( pdev ) ;
2018-06-30 03:53:32 +03:00
}
static int port_reset ( struct platform_device * pdev )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
int ret ;
mutex_lock ( & pdata - > lock ) ;
ret = __port_reset ( pdev ) ;
mutex_unlock ( & pdata - > lock ) ;
return ret ;
}
2018-06-30 03:53:31 +03:00
static int port_get_id ( struct platform_device * pdev )
{
void __iomem * base ;
base = dfl_get_feature_ioaddr_by_id ( & pdev - > dev , PORT_FEATURE_ID_HEADER ) ;
return FIELD_GET ( PORT_CAP_PORT_NUM , readq ( base + PORT_HDR_CAP ) ) ;
}
2018-06-30 03:53:32 +03:00
static ssize_t
id_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
int id = port_get_id ( to_platform_device ( dev ) ) ;
return scnprintf ( buf , PAGE_SIZE , " %d \n " , id ) ;
}
static DEVICE_ATTR_RO ( id ) ;
2019-08-04 13:20:13 +03:00
static ssize_t
ltr_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
u64 v ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
v = readq ( base + PORT_HDR_CTRL ) ;
mutex_unlock ( & pdata - > lock ) ;
return sprintf ( buf , " %x \n " , ( u8 ) FIELD_GET ( PORT_CTRL_LATENCY , v ) ) ;
}
static ssize_t
ltr_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
bool ltr ;
u64 v ;
if ( kstrtobool ( buf , & ltr ) )
return - EINVAL ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
v = readq ( base + PORT_HDR_CTRL ) ;
v & = ~ PORT_CTRL_LATENCY ;
v | = FIELD_PREP ( PORT_CTRL_LATENCY , ltr ? 1 : 0 ) ;
writeq ( v , base + PORT_HDR_CTRL ) ;
mutex_unlock ( & pdata - > lock ) ;
return count ;
}
static DEVICE_ATTR_RW ( ltr ) ;
static ssize_t
ap1_event_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
u64 v ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
v = readq ( base + PORT_HDR_STS ) ;
mutex_unlock ( & pdata - > lock ) ;
return sprintf ( buf , " %x \n " , ( u8 ) FIELD_GET ( PORT_STS_AP1_EVT , v ) ) ;
}
static ssize_t
ap1_event_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
bool clear ;
if ( kstrtobool ( buf , & clear ) | | ! clear )
return - EINVAL ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
writeq ( PORT_STS_AP1_EVT , base + PORT_HDR_STS ) ;
mutex_unlock ( & pdata - > lock ) ;
return count ;
}
static DEVICE_ATTR_RW ( ap1_event ) ;
static ssize_t
ap2_event_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
u64 v ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
v = readq ( base + PORT_HDR_STS ) ;
mutex_unlock ( & pdata - > lock ) ;
return sprintf ( buf , " %x \n " , ( u8 ) FIELD_GET ( PORT_STS_AP2_EVT , v ) ) ;
}
static ssize_t
ap2_event_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
bool clear ;
if ( kstrtobool ( buf , & clear ) | | ! clear )
return - EINVAL ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
writeq ( PORT_STS_AP2_EVT , base + PORT_HDR_STS ) ;
mutex_unlock ( & pdata - > lock ) ;
return count ;
}
static DEVICE_ATTR_RW ( ap2_event ) ;
static ssize_t
power_state_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
u64 v ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
v = readq ( base + PORT_HDR_STS ) ;
mutex_unlock ( & pdata - > lock ) ;
return sprintf ( buf , " 0x%x \n " , ( u8 ) FIELD_GET ( PORT_STS_PWR_STATE , v ) ) ;
}
static DEVICE_ATTR_RO ( power_state ) ;
2019-08-12 05:49:59 +03:00
static ssize_t
userclk_freqcmd_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
u64 userclk_freq_cmd ;
void __iomem * base ;
if ( kstrtou64 ( buf , 0 , & userclk_freq_cmd ) )
return - EINVAL ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
writeq ( userclk_freq_cmd , base + PORT_HDR_USRCLK_CMD0 ) ;
mutex_unlock ( & pdata - > lock ) ;
return count ;
}
static DEVICE_ATTR_WO ( userclk_freqcmd ) ;
static ssize_t
userclk_freqcntrcmd_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
u64 userclk_freqcntr_cmd ;
void __iomem * base ;
if ( kstrtou64 ( buf , 0 , & userclk_freqcntr_cmd ) )
return - EINVAL ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
writeq ( userclk_freqcntr_cmd , base + PORT_HDR_USRCLK_CMD1 ) ;
mutex_unlock ( & pdata - > lock ) ;
return count ;
}
static DEVICE_ATTR_WO ( userclk_freqcntrcmd ) ;
static ssize_t
userclk_freqsts_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
u64 userclk_freqsts ;
void __iomem * base ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
userclk_freqsts = readq ( base + PORT_HDR_USRCLK_STS0 ) ;
mutex_unlock ( & pdata - > lock ) ;
return sprintf ( buf , " 0x%llx \n " , ( unsigned long long ) userclk_freqsts ) ;
}
static DEVICE_ATTR_RO ( userclk_freqsts ) ;
static ssize_t
userclk_freqcntrsts_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
u64 userclk_freqcntrsts ;
void __iomem * base ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
mutex_lock ( & pdata - > lock ) ;
userclk_freqcntrsts = readq ( base + PORT_HDR_USRCLK_STS1 ) ;
mutex_unlock ( & pdata - > lock ) ;
return sprintf ( buf , " 0x%llx \n " ,
( unsigned long long ) userclk_freqcntrsts ) ;
}
static DEVICE_ATTR_RO ( userclk_freqcntrsts ) ;
2019-07-04 08:56:45 +03:00
static struct attribute * port_hdr_attrs [ ] = {
2018-06-30 03:53:32 +03:00
& dev_attr_id . attr ,
2019-08-04 13:20:13 +03:00
& dev_attr_ltr . attr ,
& dev_attr_ap1_event . attr ,
& dev_attr_ap2_event . attr ,
& dev_attr_power_state . attr ,
2019-08-12 05:49:59 +03:00
& dev_attr_userclk_freqcmd . attr ,
& dev_attr_userclk_freqcntrcmd . attr ,
& dev_attr_userclk_freqsts . attr ,
& dev_attr_userclk_freqcntrsts . attr ,
2018-06-30 03:53:32 +03:00
NULL ,
} ;
2019-08-12 05:49:58 +03:00
2019-08-12 05:49:59 +03:00
static umode_t port_hdr_attrs_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
{
struct device * dev = kobj_to_dev ( kobj ) ;
umode_t mode = attr - > mode ;
void __iomem * base ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_HEADER ) ;
if ( dfl_feature_revision ( base ) > 0 ) {
/*
* userclk sysfs interfaces are only visible in case port
* revision is 0 , as hardware with revision > 0 doesn ' t
* support this .
*/
if ( attr = = & dev_attr_userclk_freqcmd . attr | |
attr = = & dev_attr_userclk_freqcntrcmd . attr | |
attr = = & dev_attr_userclk_freqsts . attr | |
attr = = & dev_attr_userclk_freqcntrsts . attr )
mode = 0 ;
}
return mode ;
}
2019-08-12 05:49:58 +03:00
static const struct attribute_group port_hdr_group = {
2019-08-12 05:49:59 +03:00
. attrs = port_hdr_attrs ,
. is_visible = port_hdr_attrs_visible ,
2019-08-12 05:49:58 +03:00
} ;
2018-06-30 03:53:32 +03:00
2018-06-30 03:53:30 +03:00
static int port_hdr_init ( struct platform_device * pdev ,
struct dfl_feature * feature )
{
2018-06-30 03:53:32 +03:00
port_reset ( pdev ) ;
2019-08-12 05:49:58 +03:00
return 0 ;
2018-06-30 03:53:32 +03:00
}
static long
port_hdr_ioctl ( struct platform_device * pdev , struct dfl_feature * feature ,
unsigned int cmd , unsigned long arg )
{
long ret ;
switch ( cmd ) {
case DFL_FPGA_PORT_RESET :
if ( ! arg )
ret = port_reset ( pdev ) ;
else
ret = - EINVAL ;
break ;
default :
dev_dbg ( & pdev - > dev , " %x cmd not handled " , cmd ) ;
ret = - ENODEV ;
}
return ret ;
2018-06-30 03:53:30 +03:00
}
2019-08-04 13:20:15 +03:00
static const struct dfl_feature_id port_hdr_id_table [ ] = {
{ . id = PORT_FEATURE_ID_HEADER , } ,
{ 0 , }
} ;
2018-06-30 03:53:30 +03:00
static const struct dfl_feature_ops port_hdr_ops = {
. init = port_hdr_init ,
2018-06-30 03:53:32 +03:00
. ioctl = port_hdr_ioctl ,
2018-06-30 03:53:30 +03:00
} ;
2018-06-30 03:53:34 +03:00
static ssize_t
afu_id_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( dev ) ;
void __iomem * base ;
u64 guidl , guidh ;
base = dfl_get_feature_ioaddr_by_id ( dev , PORT_FEATURE_ID_AFU ) ;
mutex_lock ( & pdata - > lock ) ;
if ( pdata - > disable_count ) {
mutex_unlock ( & pdata - > lock ) ;
return - EBUSY ;
}
guidl = readq ( base + GUID_L ) ;
guidh = readq ( base + GUID_H ) ;
mutex_unlock ( & pdata - > lock ) ;
return scnprintf ( buf , PAGE_SIZE , " %016llx%016llx \n " , guidh , guidl ) ;
}
static DEVICE_ATTR_RO ( afu_id ) ;
2019-07-04 08:56:45 +03:00
static struct attribute * port_afu_attrs [ ] = {
2018-06-30 03:53:34 +03:00
& dev_attr_afu_id . attr ,
NULL
} ;
2019-08-12 05:49:58 +03:00
static umode_t port_afu_attrs_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
2018-06-30 03:53:34 +03:00
{
2019-08-12 05:49:58 +03:00
struct device * dev = kobj_to_dev ( kobj ) ;
2018-06-30 03:53:34 +03:00
2019-08-12 05:49:58 +03:00
/*
* sysfs entries are visible only if related private feature is
* enumerated .
*/
if ( ! dfl_get_feature_by_id ( dev , PORT_FEATURE_ID_AFU ) )
return 0 ;
2018-06-30 03:53:34 +03:00
2019-08-12 05:49:58 +03:00
return attr - > mode ;
2018-06-30 03:53:34 +03:00
}
2019-08-12 05:49:58 +03:00
static const struct attribute_group port_afu_group = {
. attrs = port_afu_attrs ,
. is_visible = port_afu_attrs_visible ,
} ;
static int port_afu_init ( struct platform_device * pdev ,
struct dfl_feature * feature )
2018-06-30 03:53:34 +03:00
{
2019-08-12 05:49:58 +03:00
struct resource * res = & pdev - > resource [ feature - > resource_index ] ;
2018-06-30 03:53:34 +03:00
2019-08-12 05:49:58 +03:00
return afu_mmio_region_add ( dev_get_platdata ( & pdev - > dev ) ,
DFL_PORT_REGION_INDEX_AFU ,
resource_size ( res ) , res - > start ,
DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
DFL_PORT_REGION_WRITE ) ;
2018-06-30 03:53:34 +03:00
}
2019-08-04 13:20:15 +03:00
static const struct dfl_feature_id port_afu_id_table [ ] = {
{ . id = PORT_FEATURE_ID_AFU , } ,
{ 0 , }
} ;
2018-06-30 03:53:34 +03:00
static const struct dfl_feature_ops port_afu_ops = {
. init = port_afu_init ,
} ;
2019-08-12 05:50:02 +03:00
static int port_stp_init ( struct platform_device * pdev ,
struct dfl_feature * feature )
{
struct resource * res = & pdev - > resource [ feature - > resource_index ] ;
return afu_mmio_region_add ( dev_get_platdata ( & pdev - > dev ) ,
DFL_PORT_REGION_INDEX_STP ,
resource_size ( res ) , res - > start ,
DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
DFL_PORT_REGION_WRITE ) ;
}
static const struct dfl_feature_id port_stp_id_table [ ] = {
{ . id = PORT_FEATURE_ID_STP , } ,
{ 0 , }
} ;
static const struct dfl_feature_ops port_stp_ops = {
. init = port_stp_init ,
} ;
2020-06-16 07:08:47 +03:00
static long
port_uint_ioctl ( struct platform_device * pdev , struct dfl_feature * feature ,
unsigned int cmd , unsigned long arg )
{
switch ( cmd ) {
case DFL_FPGA_PORT_UINT_GET_IRQ_NUM :
return dfl_feature_ioctl_get_num_irqs ( pdev , feature , arg ) ;
case DFL_FPGA_PORT_UINT_SET_IRQ :
return dfl_feature_ioctl_set_irq ( pdev , feature , arg ) ;
default :
dev_dbg ( & pdev - > dev , " %x cmd not handled " , cmd ) ;
return - ENODEV ;
}
}
static const struct dfl_feature_id port_uint_id_table [ ] = {
{ . id = PORT_FEATURE_ID_UINT , } ,
{ 0 , }
} ;
static const struct dfl_feature_ops port_uint_ops = {
. ioctl = port_uint_ioctl ,
} ;
2018-06-30 03:53:30 +03:00
static struct dfl_feature_driver port_feature_drvs [ ] = {
{
2019-08-04 13:20:15 +03:00
. id_table = port_hdr_id_table ,
2018-06-30 03:53:30 +03:00
. ops = & port_hdr_ops ,
} ,
2018-06-30 03:53:34 +03:00
{
2019-08-04 13:20:15 +03:00
. id_table = port_afu_id_table ,
2018-06-30 03:53:34 +03:00
. ops = & port_afu_ops ,
} ,
2019-08-12 05:50:01 +03:00
{
. id_table = port_err_id_table ,
. ops = & port_err_ops ,
} ,
2019-08-12 05:50:02 +03:00
{
. id_table = port_stp_id_table ,
. ops = & port_stp_ops ,
} ,
2020-06-16 07:08:47 +03:00
{
. id_table = port_uint_id_table ,
. ops = & port_uint_ops ,
} ,
2018-06-30 03:53:30 +03:00
{
. ops = NULL ,
}
} ;
static int afu_open ( struct inode * inode , struct file * filp )
{
struct platform_device * fdev = dfl_fpga_inode_to_feature_dev ( inode ) ;
struct dfl_feature_platform_data * pdata ;
int ret ;
pdata = dev_get_platdata ( & fdev - > dev ) ;
if ( WARN_ON ( ! pdata ) )
return - ENODEV ;
2019-11-18 08:20:41 +03:00
mutex_lock ( & pdata - > lock ) ;
ret = dfl_feature_dev_use_begin ( pdata , filp - > f_flags & O_EXCL ) ;
if ( ! ret ) {
dev_dbg ( & fdev - > dev , " Device File Opened %d Times \n " ,
dfl_feature_dev_use_count ( pdata ) ) ;
filp - > private_data = fdev ;
}
mutex_unlock ( & pdata - > lock ) ;
2018-06-30 03:53:30 +03:00
2019-11-18 08:20:41 +03:00
return ret ;
2018-06-30 03:53:30 +03:00
}
static int afu_release ( struct inode * inode , struct file * filp )
{
struct platform_device * pdev = filp - > private_data ;
struct dfl_feature_platform_data * pdata ;
2020-06-16 07:08:45 +03:00
struct dfl_feature * feature ;
2018-06-30 03:53:30 +03:00
dev_dbg ( & pdev - > dev , " Device File Release \n " ) ;
pdata = dev_get_platdata ( & pdev - > dev ) ;
2018-06-30 03:53:35 +03:00
mutex_lock ( & pdata - > lock ) ;
2018-06-30 03:53:30 +03:00
dfl_feature_dev_use_end ( pdata ) ;
2019-11-18 08:20:41 +03:00
if ( ! dfl_feature_dev_use_count ( pdata ) ) {
2020-06-16 07:08:45 +03:00
dfl_fpga_dev_for_each_feature ( pdata , feature )
dfl_fpga_set_irq_triggers ( feature , 0 ,
feature - > nr_irqs , NULL ) ;
2019-11-18 08:20:41 +03:00
__port_reset ( pdev ) ;
afu_dma_region_destroy ( pdata ) ;
}
mutex_unlock ( & pdata - > lock ) ;
2018-06-30 03:53:30 +03:00
return 0 ;
}
2018-06-30 03:53:33 +03:00
static long afu_ioctl_check_extension ( struct dfl_feature_platform_data * pdata ,
unsigned long arg )
{
/* No extension support for now */
return 0 ;
}
2018-06-30 03:53:34 +03:00
static long
afu_ioctl_get_info ( struct dfl_feature_platform_data * pdata , void __user * arg )
{
struct dfl_fpga_port_info info ;
struct dfl_afu * afu ;
unsigned long minsz ;
minsz = offsetofend ( struct dfl_fpga_port_info , num_umsgs ) ;
if ( copy_from_user ( & info , arg , minsz ) )
return - EFAULT ;
if ( info . argsz < minsz )
return - EINVAL ;
mutex_lock ( & pdata - > lock ) ;
afu = dfl_fpga_pdata_get_private ( pdata ) ;
info . flags = 0 ;
info . num_regions = afu - > num_regions ;
info . num_umsgs = afu - > num_umsgs ;
mutex_unlock ( & pdata - > lock ) ;
if ( copy_to_user ( arg , & info , sizeof ( info ) ) )
return - EFAULT ;
return 0 ;
}
static long afu_ioctl_get_region_info ( struct dfl_feature_platform_data * pdata ,
void __user * arg )
{
struct dfl_fpga_port_region_info rinfo ;
struct dfl_afu_mmio_region region ;
unsigned long minsz ;
long ret ;
minsz = offsetofend ( struct dfl_fpga_port_region_info , offset ) ;
if ( copy_from_user ( & rinfo , arg , minsz ) )
return - EFAULT ;
if ( rinfo . argsz < minsz | | rinfo . padding )
return - EINVAL ;
ret = afu_mmio_region_get_by_index ( pdata , rinfo . index , & region ) ;
if ( ret )
return ret ;
rinfo . flags = region . flags ;
rinfo . size = region . size ;
rinfo . offset = region . offset ;
if ( copy_to_user ( arg , & rinfo , sizeof ( rinfo ) ) )
return - EFAULT ;
return 0 ;
}
2018-06-30 03:53:35 +03:00
static long
afu_ioctl_dma_map ( struct dfl_feature_platform_data * pdata , void __user * arg )
{
struct dfl_fpga_port_dma_map map ;
unsigned long minsz ;
long ret ;
minsz = offsetofend ( struct dfl_fpga_port_dma_map , iova ) ;
if ( copy_from_user ( & map , arg , minsz ) )
return - EFAULT ;
if ( map . argsz < minsz | | map . flags )
return - EINVAL ;
ret = afu_dma_map_region ( pdata , map . user_addr , map . length , & map . iova ) ;
if ( ret )
return ret ;
if ( copy_to_user ( arg , & map , sizeof ( map ) ) ) {
afu_dma_unmap_region ( pdata , map . iova ) ;
return - EFAULT ;
}
dev_dbg ( & pdata - > dev - > dev , " dma map: ua=%llx, len=%llx, iova=%llx \n " ,
( unsigned long long ) map . user_addr ,
( unsigned long long ) map . length ,
( unsigned long long ) map . iova ) ;
return 0 ;
}
static long
afu_ioctl_dma_unmap ( struct dfl_feature_platform_data * pdata , void __user * arg )
{
struct dfl_fpga_port_dma_unmap unmap ;
unsigned long minsz ;
minsz = offsetofend ( struct dfl_fpga_port_dma_unmap , iova ) ;
if ( copy_from_user ( & unmap , arg , minsz ) )
return - EFAULT ;
if ( unmap . argsz < minsz | | unmap . flags )
return - EINVAL ;
return afu_dma_unmap_region ( pdata , unmap . iova ) ;
}
2018-06-30 03:53:30 +03:00
static long afu_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
{
struct platform_device * pdev = filp - > private_data ;
struct dfl_feature_platform_data * pdata ;
struct dfl_feature * f ;
long ret ;
dev_dbg ( & pdev - > dev , " %s cmd 0x%x \n " , __func__ , cmd ) ;
pdata = dev_get_platdata ( & pdev - > dev ) ;
switch ( cmd ) {
2018-06-30 03:53:33 +03:00
case DFL_FPGA_GET_API_VERSION :
return DFL_FPGA_API_VERSION ;
case DFL_FPGA_CHECK_EXTENSION :
return afu_ioctl_check_extension ( pdata , arg ) ;
2018-06-30 03:53:34 +03:00
case DFL_FPGA_PORT_GET_INFO :
return afu_ioctl_get_info ( pdata , ( void __user * ) arg ) ;
case DFL_FPGA_PORT_GET_REGION_INFO :
return afu_ioctl_get_region_info ( pdata , ( void __user * ) arg ) ;
2018-06-30 03:53:35 +03:00
case DFL_FPGA_PORT_DMA_MAP :
return afu_ioctl_dma_map ( pdata , ( void __user * ) arg ) ;
case DFL_FPGA_PORT_DMA_UNMAP :
return afu_ioctl_dma_unmap ( pdata , ( void __user * ) arg ) ;
2018-06-30 03:53:30 +03:00
default :
/*
* Let sub - feature ' s ioctl function to handle the cmd
* Sub - feature ' s ioctl returns - ENODEV when cmd is not
* handled in this sub feature , and returns 0 and other
* error code if cmd is handled .
*/
dfl_fpga_dev_for_each_feature ( pdata , f )
if ( f - > ops & & f - > ops - > ioctl ) {
ret = f - > ops - > ioctl ( pdev , f , cmd , arg ) ;
if ( ret ! = - ENODEV )
return ret ;
}
}
return - EINVAL ;
}
2020-03-09 21:53:23 +03:00
static const struct vm_operations_struct afu_vma_ops = {
# ifdef CONFIG_HAVE_IOREMAP_PROT
. access = generic_access_phys ,
# endif
} ;
2018-06-30 03:53:34 +03:00
static int afu_mmap ( struct file * filp , struct vm_area_struct * vma )
{
struct platform_device * pdev = filp - > private_data ;
struct dfl_feature_platform_data * pdata ;
u64 size = vma - > vm_end - vma - > vm_start ;
struct dfl_afu_mmio_region region ;
u64 offset ;
int ret ;
if ( ! ( vma - > vm_flags & VM_SHARED ) )
return - EINVAL ;
pdata = dev_get_platdata ( & pdev - > dev ) ;
offset = vma - > vm_pgoff < < PAGE_SHIFT ;
ret = afu_mmio_region_get_by_offset ( pdata , offset , size , & region ) ;
if ( ret )
return ret ;
if ( ! ( region . flags & DFL_PORT_REGION_MMAP ) )
return - EINVAL ;
if ( ( vma - > vm_flags & VM_READ ) & & ! ( region . flags & DFL_PORT_REGION_READ ) )
return - EPERM ;
if ( ( vma - > vm_flags & VM_WRITE ) & &
! ( region . flags & DFL_PORT_REGION_WRITE ) )
return - EPERM ;
2020-03-09 21:53:23 +03:00
/* Support debug access to the mapping */
vma - > vm_ops = & afu_vma_ops ;
2018-06-30 03:53:34 +03:00
vma - > vm_page_prot = pgprot_noncached ( vma - > vm_page_prot ) ;
return remap_pfn_range ( vma , vma - > vm_start ,
( region . phys + ( offset - region . offset ) ) > > PAGE_SHIFT ,
size , vma - > vm_page_prot ) ;
}
2018-06-30 03:53:30 +03:00
static const struct file_operations afu_fops = {
. owner = THIS_MODULE ,
. open = afu_open ,
. release = afu_release ,
. unlocked_ioctl = afu_ioctl ,
2018-06-30 03:53:34 +03:00
. mmap = afu_mmap ,
2018-06-30 03:53:30 +03:00
} ;
2018-06-30 03:53:34 +03:00
static int afu_dev_init ( struct platform_device * pdev )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
struct dfl_afu * afu ;
afu = devm_kzalloc ( & pdev - > dev , sizeof ( * afu ) , GFP_KERNEL ) ;
if ( ! afu )
return - ENOMEM ;
afu - > pdata = pdata ;
mutex_lock ( & pdata - > lock ) ;
dfl_fpga_pdata_set_private ( pdata , afu ) ;
afu_mmio_region_init ( pdata ) ;
2018-06-30 03:53:35 +03:00
afu_dma_region_init ( pdata ) ;
2018-06-30 03:53:34 +03:00
mutex_unlock ( & pdata - > lock ) ;
return 0 ;
}
static int afu_dev_destroy ( struct platform_device * pdev )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
mutex_lock ( & pdata - > lock ) ;
afu_mmio_region_destroy ( pdata ) ;
2018-06-30 03:53:35 +03:00
afu_dma_region_destroy ( pdata ) ;
2018-06-30 03:53:34 +03:00
dfl_fpga_pdata_set_private ( pdata , NULL ) ;
mutex_unlock ( & pdata - > lock ) ;
return 0 ;
}
2018-06-30 03:53:31 +03:00
static int port_enable_set ( struct platform_device * pdev , bool enable )
{
struct dfl_feature_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2021-03-24 01:46:50 +03:00
int ret ;
2018-06-30 03:53:31 +03:00
mutex_lock ( & pdata - > lock ) ;
if ( enable )
2021-03-24 01:46:50 +03:00
ret = __afu_port_enable ( pdev ) ;
2018-06-30 03:53:31 +03:00
else
2019-08-12 05:50:00 +03:00
ret = __afu_port_disable ( pdev ) ;
2018-06-30 03:53:31 +03:00
mutex_unlock ( & pdata - > lock ) ;
return ret ;
}
static struct dfl_fpga_port_ops afu_port_ops = {
. name = DFL_FPGA_FEATURE_DEV_PORT ,
. owner = THIS_MODULE ,
. get_id = port_get_id ,
. enable_set = port_enable_set ,
} ;
2018-06-30 03:53:30 +03:00
static int afu_probe ( struct platform_device * pdev )
{
int ret ;
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
2018-06-30 03:53:34 +03:00
ret = afu_dev_init ( pdev ) ;
if ( ret )
goto exit ;
2018-06-30 03:53:30 +03:00
ret = dfl_fpga_dev_feature_init ( pdev , port_feature_drvs ) ;
if ( ret )
2018-06-30 03:53:34 +03:00
goto dev_destroy ;
2018-06-30 03:53:30 +03:00
ret = dfl_fpga_dev_ops_register ( pdev , & afu_fops , THIS_MODULE ) ;
2018-06-30 03:53:34 +03:00
if ( ret ) {
2018-06-30 03:53:30 +03:00
dfl_fpga_dev_feature_uinit ( pdev ) ;
2018-06-30 03:53:34 +03:00
goto dev_destroy ;
}
return 0 ;
2018-06-30 03:53:30 +03:00
2018-06-30 03:53:34 +03:00
dev_destroy :
afu_dev_destroy ( pdev ) ;
exit :
2018-06-30 03:53:30 +03:00
return ret ;
}
static int afu_remove ( struct platform_device * pdev )
{
dev_dbg ( & pdev - > dev , " %s \n " , __func__ ) ;
dfl_fpga_dev_ops_unregister ( pdev ) ;
dfl_fpga_dev_feature_uinit ( pdev ) ;
2018-06-30 03:53:34 +03:00
afu_dev_destroy ( pdev ) ;
2018-06-30 03:53:30 +03:00
return 0 ;
}
2019-08-12 05:49:58 +03:00
static const struct attribute_group * afu_dev_groups [ ] = {
& port_hdr_group ,
& port_afu_group ,
2019-08-12 05:50:01 +03:00
& port_err_group ,
2019-08-12 05:49:58 +03:00
NULL
} ;
2018-06-30 03:53:30 +03:00
static struct platform_driver afu_driver = {
. driver = {
2019-08-12 05:49:58 +03:00
. name = DFL_FPGA_FEATURE_DEV_PORT ,
. dev_groups = afu_dev_groups ,
2018-06-30 03:53:30 +03:00
} ,
. probe = afu_probe ,
. remove = afu_remove ,
} ;
2018-06-30 03:53:31 +03:00
static int __init afu_init ( void )
{
int ret ;
dfl_fpga_port_ops_add ( & afu_port_ops ) ;
ret = platform_driver_register ( & afu_driver ) ;
if ( ret )
dfl_fpga_port_ops_del ( & afu_port_ops ) ;
return ret ;
}
static void __exit afu_exit ( void )
{
platform_driver_unregister ( & afu_driver ) ;
dfl_fpga_port_ops_del ( & afu_port_ops ) ;
}
module_init ( afu_init ) ;
module_exit ( afu_exit ) ;
2018-06-30 03:53:30 +03:00
MODULE_DESCRIPTION ( " FPGA Accelerated Function Unit driver " ) ;
MODULE_AUTHOR ( " Intel Corporation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:dfl-port " ) ;