2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2018-03-26 19:30:51 +03:00
/*
* CXL Flash Device Driver
*
* Written by : Matthew R . Ochs < mrochs @ linux . vnet . ibm . com > , IBM Corporation
* Uma Krishnan < ukrishn @ linux . vnet . ibm . com > , IBM Corporation
*
* Copyright ( C ) 2018 IBM Corporation
*/
2018-03-26 19:32:09 +03:00
# include <linux/file.h>
2018-03-26 19:32:00 +03:00
# include <linux/idr.h>
2018-03-26 19:32:09 +03:00
# include <linux/module.h>
# include <linux/mount.h>
2019-03-25 19:38:25 +03:00
# include <linux/pseudo_fs.h>
2018-03-26 19:34:03 +03:00
# include <linux/poll.h>
2018-03-26 19:34:12 +03:00
# include <linux/sched/signal.h>
2018-03-26 19:32:00 +03:00
2018-03-26 19:30:51 +03:00
# include <misc/ocxl.h>
2018-03-26 19:34:12 +03:00
# include <uapi/misc/cxl.h>
2018-03-26 19:30:51 +03:00
# include "backend.h"
2018-03-26 19:31:01 +03:00
# include "ocxl_hw.h"
2018-03-26 19:32:09 +03:00
/*
* Pseudo - filesystem to allocate inodes .
*/
# define OCXLFLASH_FS_MAGIC 0x1697698f
static int ocxlflash_fs_cnt ;
static struct vfsmount * ocxlflash_vfs_mount ;
2019-03-25 19:38:25 +03:00
static int ocxlflash_fs_init_fs_context ( struct fs_context * fc )
2018-03-26 19:32:09 +03:00
{
2019-03-25 19:38:25 +03:00
return init_pseudo ( fc , OCXLFLASH_FS_MAGIC ) ? 0 : - ENOMEM ;
2018-03-26 19:32:09 +03:00
}
static struct file_system_type ocxlflash_fs_type = {
. name = " ocxlflash " ,
. owner = THIS_MODULE ,
2019-03-25 19:38:25 +03:00
. init_fs_context = ocxlflash_fs_init_fs_context ,
2018-03-26 19:32:09 +03:00
. kill_sb = kill_anon_super ,
} ;
/*
* ocxlflash_release_mapping ( ) - release the memory mapping
* @ ctx : Context whose mapping is to be released .
*/
static void ocxlflash_release_mapping ( struct ocxlflash_context * ctx )
{
if ( ctx - > mapping )
simple_release_fs ( & ocxlflash_vfs_mount , & ocxlflash_fs_cnt ) ;
ctx - > mapping = NULL ;
}
/*
* ocxlflash_getfile ( ) - allocate pseudo filesystem , inode , and the file
* @ dev : Generic device of the host .
* @ name : Name of the pseudo filesystem .
* @ fops : File operations .
* @ priv : Private data .
* @ flags : Flags for the file .
*
* Return : pointer to the file on success , ERR_PTR on failure
*/
static struct file * ocxlflash_getfile ( struct device * dev , const char * name ,
const struct file_operations * fops ,
void * priv , int flags )
{
struct file * file ;
2018-06-17 19:42:49 +03:00
struct inode * inode ;
2018-03-26 19:32:09 +03:00
int rc ;
if ( fops - > owner & & ! try_module_get ( fops - > owner ) ) {
dev_err ( dev , " %s: Owner does not exist \n " , __func__ ) ;
rc = - ENOENT ;
goto err1 ;
}
rc = simple_pin_fs ( & ocxlflash_fs_type , & ocxlflash_vfs_mount ,
& ocxlflash_fs_cnt ) ;
if ( unlikely ( rc < 0 ) ) {
dev_err ( dev , " %s: Cannot mount ocxlflash pseudofs rc=%d \n " ,
__func__ , rc ) ;
goto err2 ;
}
inode = alloc_anon_inode ( ocxlflash_vfs_mount - > mnt_sb ) ;
if ( IS_ERR ( inode ) ) {
rc = PTR_ERR ( inode ) ;
dev_err ( dev , " %s: alloc_anon_inode failed rc=%d \n " ,
__func__ , rc ) ;
goto err3 ;
}
2018-06-17 19:42:49 +03:00
file = alloc_file_pseudo ( inode , ocxlflash_vfs_mount , name ,
flags & ( O_ACCMODE | O_NONBLOCK ) , fops ) ;
2018-03-26 19:32:09 +03:00
if ( IS_ERR ( file ) ) {
rc = PTR_ERR ( file ) ;
dev_err ( dev , " %s: alloc_file failed rc=%d \n " ,
__func__ , rc ) ;
2018-06-17 19:42:49 +03:00
goto err4 ;
2018-03-26 19:32:09 +03:00
}
file - > private_data = priv ;
out :
return file ;
err4 :
iput ( inode ) ;
err3 :
simple_release_fs ( & ocxlflash_vfs_mount , & ocxlflash_fs_cnt ) ;
err2 :
module_put ( fops - > owner ) ;
err1 :
file = ERR_PTR ( rc ) ;
goto out ;
}
2018-03-26 19:32:56 +03:00
/**
* ocxlflash_psa_map ( ) - map the process specific MMIO space
* @ ctx_cookie : Adapter context for which the mapping needs to be done .
*
* Return : MMIO pointer of the mapped region
*/
static void __iomem * ocxlflash_psa_map ( void * ctx_cookie )
{
struct ocxlflash_context * ctx = ctx_cookie ;
2018-03-26 19:35:00 +03:00
struct device * dev = ctx - > hw_afu - > dev ;
mutex_lock ( & ctx - > state_mutex ) ;
if ( ctx - > state ! = STARTED ) {
dev_err ( dev , " %s: Context not started, state=%d \n " , __func__ ,
ctx - > state ) ;
mutex_unlock ( & ctx - > state_mutex ) ;
return NULL ;
}
mutex_unlock ( & ctx - > state_mutex ) ;
2018-03-26 19:32:56 +03:00
return ioremap ( ctx - > psn_phys , ctx - > psn_size ) ;
}
/**
* ocxlflash_psa_unmap ( ) - unmap the process specific MMIO space
* @ addr : MMIO pointer to unmap .
*/
static void ocxlflash_psa_unmap ( void __iomem * addr )
{
iounmap ( addr ) ;
}
2018-03-26 19:32:20 +03:00
/**
* ocxlflash_process_element ( ) - get process element of the adapter context
* @ ctx_cookie : Adapter context associated with the process element .
*
* Return : process element of the adapter context
*/
static int ocxlflash_process_element ( void * ctx_cookie )
{
struct ocxlflash_context * ctx = ctx_cookie ;
return ctx - > pe ;
}
2018-03-26 19:33:48 +03:00
/**
* afu_map_irq ( ) - map the interrupt of the adapter context
* @ flags : Flags .
* @ ctx : Adapter context .
* @ num : Per - context AFU interrupt number .
* @ handler : Interrupt handler to register .
* @ cookie : Interrupt handler private data .
* @ name : Name of the interrupt .
*
* Return : 0 on success , - errno on failure
*/
static int afu_map_irq ( u64 flags , struct ocxlflash_context * ctx , int num ,
irq_handler_t handler , void * cookie , char * name )
{
struct ocxl_hw_afu * afu = ctx - > hw_afu ;
struct device * dev = afu - > dev ;
struct ocxlflash_irqs * irq ;
void __iomem * vtrig ;
u32 virq ;
int rc = 0 ;
if ( num < 0 | | num > = ctx - > num_irqs ) {
dev_err ( dev , " %s: Interrupt %d not allocated \n " , __func__ , num ) ;
rc = - ENOENT ;
goto out ;
}
irq = & ctx - > irqs [ num ] ;
virq = irq_create_mapping ( NULL , irq - > hwirq ) ;
if ( unlikely ( ! virq ) ) {
dev_err ( dev , " %s: irq_create_mapping failed \n " , __func__ ) ;
rc = - ENOMEM ;
goto out ;
}
rc = request_irq ( virq , handler , 0 , name , cookie ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: request_irq failed rc=%d \n " , __func__ , rc ) ;
goto err1 ;
}
vtrig = ioremap ( irq - > ptrig , PAGE_SIZE ) ;
if ( unlikely ( ! vtrig ) ) {
dev_err ( dev , " %s: Trigger page mapping failed \n " , __func__ ) ;
rc = - ENOMEM ;
goto err2 ;
}
irq - > virq = virq ;
irq - > vtrig = vtrig ;
out :
return rc ;
err2 :
free_irq ( virq , cookie ) ;
err1 :
irq_dispose_mapping ( virq ) ;
goto out ;
}
/**
* ocxlflash_map_afu_irq ( ) - map the interrupt of the adapter context
* @ ctx_cookie : Adapter context .
* @ num : Per - context AFU interrupt number .
* @ handler : Interrupt handler to register .
* @ cookie : Interrupt handler private data .
* @ name : Name of the interrupt .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_map_afu_irq ( void * ctx_cookie , int num ,
irq_handler_t handler , void * cookie ,
char * name )
{
return afu_map_irq ( 0 , ctx_cookie , num , handler , cookie , name ) ;
}
/**
* afu_unmap_irq ( ) - unmap the interrupt
* @ flags : Flags .
* @ ctx : Adapter context .
* @ num : Per - context AFU interrupt number .
* @ cookie : Interrupt handler private data .
*/
static void afu_unmap_irq ( u64 flags , struct ocxlflash_context * ctx , int num ,
void * cookie )
{
struct ocxl_hw_afu * afu = ctx - > hw_afu ;
struct device * dev = afu - > dev ;
struct ocxlflash_irqs * irq ;
if ( num < 0 | | num > = ctx - > num_irqs ) {
dev_err ( dev , " %s: Interrupt %d not allocated \n " , __func__ , num ) ;
return ;
}
irq = & ctx - > irqs [ num ] ;
if ( irq - > vtrig )
iounmap ( irq - > vtrig ) ;
if ( irq_find_mapping ( NULL , irq - > hwirq ) ) {
free_irq ( irq - > virq , cookie ) ;
irq_dispose_mapping ( irq - > virq ) ;
}
memset ( irq , 0 , sizeof ( * irq ) ) ;
}
/**
* ocxlflash_unmap_afu_irq ( ) - unmap the interrupt
* @ ctx_cookie : Adapter context .
* @ num : Per - context AFU interrupt number .
* @ cookie : Interrupt handler private data .
*/
static void ocxlflash_unmap_afu_irq ( void * ctx_cookie , int num , void * cookie )
{
return afu_unmap_irq ( 0 , ctx_cookie , num , cookie ) ;
}
2018-03-26 19:34:35 +03:00
/**
* ocxlflash_get_irq_objhndl ( ) - get the object handle for an interrupt
* @ ctx_cookie : Context associated with the interrupt .
* @ irq : Interrupt number .
*
* Return : effective address of the mapped region
*/
static u64 ocxlflash_get_irq_objhndl ( void * ctx_cookie , int irq )
{
struct ocxlflash_context * ctx = ctx_cookie ;
if ( irq < 0 | | irq > = ctx - > num_irqs )
return 0 ;
return ( __force u64 ) ctx - > irqs [ irq ] . vtrig ;
}
2018-03-26 19:35:07 +03:00
/**
* ocxlflash_xsl_fault ( ) - callback when translation error is triggered
* @ data : Private data provided at callback registration , the context .
* @ addr : Address that triggered the error .
* @ dsisr : Value of dsisr register .
*/
static void ocxlflash_xsl_fault ( void * data , u64 addr , u64 dsisr )
{
struct ocxlflash_context * ctx = data ;
spin_lock ( & ctx - > slock ) ;
ctx - > fault_addr = addr ;
ctx - > fault_dsisr = dsisr ;
ctx - > pending_fault = true ;
spin_unlock ( & ctx - > slock ) ;
wake_up_all ( & ctx - > wq ) ;
}
2018-03-26 19:32:48 +03:00
/**
* start_context ( ) - local routine to start a context
* @ ctx : Adapter context to be started .
*
2018-03-26 19:33:35 +03:00
* Assign the context specific MMIO space , add and enable the PE .
2018-03-26 19:32:48 +03:00
*
* Return : 0 on success , - errno on failure
*/
static int start_context ( struct ocxlflash_context * ctx )
{
struct ocxl_hw_afu * afu = ctx - > hw_afu ;
struct ocxl_afu_config * acfg = & afu - > acfg ;
2018-03-26 19:33:35 +03:00
void * link_token = afu - > link_token ;
struct device * dev = afu - > dev ;
2018-03-26 19:32:48 +03:00
bool master = ctx - > master ;
2018-03-26 19:33:55 +03:00
struct mm_struct * mm ;
2018-03-26 19:33:35 +03:00
int rc = 0 ;
2018-03-26 19:33:55 +03:00
u32 pid ;
2018-03-26 19:32:48 +03:00
2018-03-26 19:35:00 +03:00
mutex_lock ( & ctx - > state_mutex ) ;
if ( ctx - > state ! = OPENED ) {
dev_err ( dev , " %s: Context state invalid, state=%d \n " ,
__func__ , ctx - > state ) ;
rc = - EINVAL ;
goto out ;
}
2018-03-26 19:32:48 +03:00
if ( master ) {
ctx - > psn_size = acfg - > global_mmio_size ;
ctx - > psn_phys = afu - > gmmio_phys ;
} else {
ctx - > psn_size = acfg - > pp_mmio_stride ;
ctx - > psn_phys = afu - > ppmmio_phys + ( ctx - > pe * ctx - > psn_size ) ;
}
2018-03-26 19:33:55 +03:00
/* pid and mm not set for master contexts */
if ( master ) {
pid = 0 ;
mm = NULL ;
} else {
pid = current - > mm - > context . id ;
mm = current - > mm ;
}
2018-03-26 19:33:35 +03:00
2018-03-26 19:35:07 +03:00
rc = ocxl_link_add_pe ( link_token , ctx - > pe , pid , 0 , 0 , mm ,
ocxlflash_xsl_fault , ctx ) ;
2018-03-26 19:33:35 +03:00
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_link_add_pe failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
2018-03-26 19:35:00 +03:00
ctx - > state = STARTED ;
2018-03-26 19:33:35 +03:00
out :
2018-03-26 19:35:00 +03:00
mutex_unlock ( & ctx - > state_mutex ) ;
2018-03-26 19:33:35 +03:00
return rc ;
2018-03-26 19:32:48 +03:00
}
/**
* ocxlflash_start_context ( ) - start a kernel context
* @ ctx_cookie : Adapter context to be started .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_start_context ( void * ctx_cookie )
{
struct ocxlflash_context * ctx = ctx_cookie ;
return start_context ( ctx ) ;
}
2018-03-26 19:33:35 +03:00
/**
* ocxlflash_stop_context ( ) - stop a context
* @ ctx_cookie : Adapter context to be stopped .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_stop_context ( void * ctx_cookie )
{
struct ocxlflash_context * ctx = ctx_cookie ;
struct ocxl_hw_afu * afu = ctx - > hw_afu ;
struct ocxl_afu_config * acfg = & afu - > acfg ;
struct pci_dev * pdev = afu - > pdev ;
struct device * dev = afu - > dev ;
2018-03-26 19:35:00 +03:00
enum ocxlflash_ctx_state state ;
int rc = 0 ;
mutex_lock ( & ctx - > state_mutex ) ;
state = ctx - > state ;
ctx - > state = CLOSED ;
mutex_unlock ( & ctx - > state_mutex ) ;
if ( state ! = STARTED )
goto out ;
2018-03-26 19:33:35 +03:00
rc = ocxl_config_terminate_pasid ( pdev , acfg - > dvsec_afu_control_pos ,
ctx - > pe ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_config_terminate_pasid failed rc=%d \n " ,
__func__ , rc ) ;
/* If EBUSY, PE could be referenced in future by the AFU */
if ( rc = = - EBUSY )
goto out ;
}
rc = ocxl_link_remove_pe ( afu - > link_token , ctx - > pe ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_link_remove_pe failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
out :
return rc ;
}
2018-03-26 19:35:15 +03:00
/**
* ocxlflash_afu_reset ( ) - reset the AFU
* @ ctx_cookie : Adapter context .
*/
static int ocxlflash_afu_reset ( void * ctx_cookie )
{
struct ocxlflash_context * ctx = ctx_cookie ;
struct device * dev = ctx - > hw_afu - > dev ;
/* Pending implementation from OCXL transport services */
dev_err_once ( dev , " %s: afu_reset() fop not supported \n " , __func__ ) ;
/* Silently return success until it is implemented */
return 0 ;
}
2018-03-26 19:31:53 +03:00
/**
* ocxlflash_set_master ( ) - sets the context as master
* @ ctx_cookie : Adapter context to set as master .
*/
static void ocxlflash_set_master ( void * ctx_cookie )
{
struct ocxlflash_context * ctx = ctx_cookie ;
ctx - > master = true ;
}
/**
* ocxlflash_get_context ( ) - obtains the context associated with the host
* @ pdev : PCI device associated with the host .
* @ afu_cookie : Hardware AFU associated with the host .
*
* Return : returns the pointer to host adapter context
*/
static void * ocxlflash_get_context ( struct pci_dev * pdev , void * afu_cookie )
{
struct ocxl_hw_afu * afu = afu_cookie ;
return afu - > ocxl_ctx ;
}
/**
* ocxlflash_dev_context_init ( ) - allocate and initialize an adapter context
* @ pdev : PCI device associated with the host .
* @ afu_cookie : Hardware AFU associated with the host .
*
* Return : returns the adapter context on success , ERR_PTR on failure
*/
static void * ocxlflash_dev_context_init ( struct pci_dev * pdev , void * afu_cookie )
{
struct ocxl_hw_afu * afu = afu_cookie ;
struct device * dev = afu - > dev ;
struct ocxlflash_context * ctx ;
int rc ;
ctx = kzalloc ( sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( unlikely ( ! ctx ) ) {
dev_err ( dev , " %s: Context allocation failed \n " , __func__ ) ;
rc = - ENOMEM ;
2018-03-26 19:32:00 +03:00
goto err1 ;
}
idr_preload ( GFP_KERNEL ) ;
rc = idr_alloc ( & afu - > idr , ctx , 0 , afu - > max_pasid , GFP_NOWAIT ) ;
idr_preload_end ( ) ;
if ( unlikely ( rc < 0 ) ) {
dev_err ( dev , " %s: idr_alloc failed rc=%d \n " , __func__ , rc ) ;
goto err2 ;
2018-03-26 19:31:53 +03:00
}
2018-03-26 19:33:55 +03:00
spin_lock_init ( & ctx - > slock ) ;
2018-03-26 19:34:03 +03:00
init_waitqueue_head ( & ctx - > wq ) ;
2018-03-26 19:35:00 +03:00
mutex_init ( & ctx - > state_mutex ) ;
2018-03-26 19:33:55 +03:00
2018-03-26 19:35:00 +03:00
ctx - > state = OPENED ;
2018-03-26 19:32:00 +03:00
ctx - > pe = rc ;
2018-03-26 19:31:53 +03:00
ctx - > master = false ;
2018-03-26 19:32:09 +03:00
ctx - > mapping = NULL ;
2018-03-26 19:31:53 +03:00
ctx - > hw_afu = afu ;
2018-03-26 19:33:55 +03:00
ctx - > irq_bitmap = 0 ;
ctx - > pending_irq = false ;
2018-03-26 19:35:07 +03:00
ctx - > pending_fault = false ;
2018-03-26 19:31:53 +03:00
out :
return ctx ;
2018-03-26 19:32:00 +03:00
err2 :
kfree ( ctx ) ;
err1 :
2018-03-26 19:31:53 +03:00
ctx = ERR_PTR ( rc ) ;
goto out ;
}
/**
* ocxlflash_release_context ( ) - releases an adapter context
* @ ctx_cookie : Adapter context to be released .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_release_context ( void * ctx_cookie )
{
struct ocxlflash_context * ctx = ctx_cookie ;
2018-03-26 19:35:00 +03:00
struct device * dev ;
2018-03-26 19:31:53 +03:00
int rc = 0 ;
if ( ! ctx )
goto out ;
2018-03-26 19:35:00 +03:00
dev = ctx - > hw_afu - > dev ;
mutex_lock ( & ctx - > state_mutex ) ;
if ( ctx - > state > = STARTED ) {
dev_err ( dev , " %s: Context in use, state=%d \n " , __func__ ,
ctx - > state ) ;
mutex_unlock ( & ctx - > state_mutex ) ;
rc = - EBUSY ;
goto out ;
}
mutex_unlock ( & ctx - > state_mutex ) ;
2018-03-26 19:32:00 +03:00
idr_remove ( & ctx - > hw_afu - > idr , ctx - > pe ) ;
2018-03-26 19:32:09 +03:00
ocxlflash_release_mapping ( ctx ) ;
2018-03-26 19:31:53 +03:00
kfree ( ctx ) ;
out :
return rc ;
}
2018-03-26 19:32:29 +03:00
/**
* ocxlflash_perst_reloads_same_image ( ) - sets the image reload policy
* @ afu_cookie : Hardware AFU associated with the host .
* @ image : Whether to load the same image on PERST .
*/
static void ocxlflash_perst_reloads_same_image ( void * afu_cookie , bool image )
{
struct ocxl_hw_afu * afu = afu_cookie ;
afu - > perst_same_image = image ;
}
2018-03-26 19:33:14 +03:00
/**
* ocxlflash_read_adapter_vpd ( ) - reads the adapter VPD
* @ pdev : PCI device associated with the host .
* @ buf : Buffer to get the VPD data .
* @ count : Size of buffer ( maximum bytes that can be read ) .
*
* Return : size of VPD on success , - errno on failure
*/
static ssize_t ocxlflash_read_adapter_vpd ( struct pci_dev * pdev , void * buf ,
size_t count )
{
return pci_read_vpd ( pdev , 0 , count , buf ) ;
}
2018-03-26 19:33:41 +03:00
/**
* free_afu_irqs ( ) - internal service to free interrupts
* @ ctx : Adapter context .
*/
static void free_afu_irqs ( struct ocxlflash_context * ctx )
{
struct ocxl_hw_afu * afu = ctx - > hw_afu ;
struct device * dev = afu - > dev ;
int i ;
if ( ! ctx - > irqs ) {
dev_err ( dev , " %s: Interrupts not allocated \n " , __func__ ) ;
return ;
}
for ( i = ctx - > num_irqs ; i > = 0 ; i - - )
ocxl_link_free_irq ( afu - > link_token , ctx - > irqs [ i ] . hwirq ) ;
kfree ( ctx - > irqs ) ;
ctx - > irqs = NULL ;
}
/**
* alloc_afu_irqs ( ) - internal service to allocate interrupts
* @ ctx : Context associated with the request .
* @ num : Number of interrupts requested .
*
* Return : 0 on success , - errno on failure
*/
static int alloc_afu_irqs ( struct ocxlflash_context * ctx , int num )
{
struct ocxl_hw_afu * afu = ctx - > hw_afu ;
struct device * dev = afu - > dev ;
struct ocxlflash_irqs * irqs ;
u64 addr ;
int rc = 0 ;
int hwirq ;
int i ;
if ( ctx - > irqs ) {
dev_err ( dev , " %s: Interrupts already allocated \n " , __func__ ) ;
rc = - EEXIST ;
goto out ;
}
if ( num > OCXL_MAX_IRQS ) {
dev_err ( dev , " %s: Too many interrupts num=%d \n " , __func__ , num ) ;
rc = - EINVAL ;
goto out ;
}
irqs = kcalloc ( num , sizeof ( * irqs ) , GFP_KERNEL ) ;
if ( unlikely ( ! irqs ) ) {
dev_err ( dev , " %s: Context irqs allocation failed \n " , __func__ ) ;
rc = - ENOMEM ;
goto out ;
}
for ( i = 0 ; i < num ; i + + ) {
rc = ocxl_link_irq_alloc ( afu - > link_token , & hwirq , & addr ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_link_irq_alloc failed rc=%d \n " ,
__func__ , rc ) ;
goto err ;
}
irqs [ i ] . hwirq = hwirq ;
irqs [ i ] . ptrig = addr ;
}
ctx - > irqs = irqs ;
ctx - > num_irqs = num ;
out :
return rc ;
err :
for ( i = i - 1 ; i > = 0 ; i - - )
ocxl_link_free_irq ( afu - > link_token , irqs [ i ] . hwirq ) ;
kfree ( irqs ) ;
goto out ;
}
/**
* ocxlflash_allocate_afu_irqs ( ) - allocates the requested number of interrupts
* @ ctx_cookie : Context associated with the request .
* @ num : Number of interrupts requested .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_allocate_afu_irqs ( void * ctx_cookie , int num )
{
return alloc_afu_irqs ( ctx_cookie , num ) ;
}
/**
* ocxlflash_free_afu_irqs ( ) - frees the interrupts of an adapter context
* @ ctx_cookie : Adapter context .
*/
static void ocxlflash_free_afu_irqs ( void * ctx_cookie )
{
free_afu_irqs ( ctx_cookie ) ;
}
2018-03-26 19:32:37 +03:00
/**
* ocxlflash_unconfig_afu ( ) - unconfigure the AFU
* @ afu : AFU associated with the host .
*/
static void ocxlflash_unconfig_afu ( struct ocxl_hw_afu * afu )
{
if ( afu - > gmmio_virt ) {
iounmap ( afu - > gmmio_virt ) ;
afu - > gmmio_virt = NULL ;
}
}
2018-03-26 19:31:01 +03:00
/**
* ocxlflash_destroy_afu ( ) - destroy the AFU structure
* @ afu_cookie : AFU to be freed .
*/
static void ocxlflash_destroy_afu ( void * afu_cookie )
{
struct ocxl_hw_afu * afu = afu_cookie ;
2018-03-26 19:33:05 +03:00
int pos ;
2018-03-26 19:31:01 +03:00
if ( ! afu )
return ;
2018-03-26 19:31:53 +03:00
ocxlflash_release_context ( afu - > ocxl_ctx ) ;
2018-03-26 19:32:00 +03:00
idr_destroy ( & afu - > idr ) ;
2018-03-26 19:33:05 +03:00
/* Disable the AFU */
pos = afu - > acfg . dvsec_afu_control_pos ;
ocxl_config_set_afu_state ( afu - > pdev , pos , 0 ) ;
2018-03-26 19:32:37 +03:00
ocxlflash_unconfig_afu ( afu ) ;
2018-03-26 19:31:01 +03:00
kfree ( afu ) ;
}
2018-03-26 19:31:09 +03:00
/**
* ocxlflash_config_fn ( ) - configure the host function
* @ pdev : PCI device associated with the host .
* @ afu : AFU associated with the host .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_config_fn ( struct pci_dev * pdev , struct ocxl_hw_afu * afu )
{
struct ocxl_fn_config * fcfg = & afu - > fcfg ;
struct device * dev = & pdev - > dev ;
2018-03-26 19:31:21 +03:00
u16 base , enabled , supported ;
2018-03-26 19:31:09 +03:00
int rc = 0 ;
/* Read DVSEC config of the function */
rc = ocxl_config_read_function ( pdev , fcfg ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_config_read_function failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
/* Check if function has AFUs defined, only 1 per function supported */
if ( fcfg - > max_afu_index > = 0 ) {
afu - > is_present = true ;
if ( fcfg - > max_afu_index ! = 0 )
dev_warn ( dev , " %s: Unexpected AFU index value %d \n " ,
__func__ , fcfg - > max_afu_index ) ;
}
2018-03-26 19:31:21 +03:00
rc = ocxl_config_get_actag_info ( pdev , & base , & enabled , & supported ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_config_get_actag_info failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
afu - > fn_actag_base = base ;
afu - > fn_actag_enabled = enabled ;
ocxl_config_set_actag ( pdev , fcfg - > dvsec_function_pos , base , enabled ) ;
dev_dbg ( dev , " %s: Function acTag range base=%u enabled=%u \n " ,
__func__ , base , enabled ) ;
2018-03-26 19:33:21 +03:00
rc = ocxl_link_setup ( pdev , 0 , & afu - > link_token ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_link_setup failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
2018-03-26 19:33:28 +03:00
rc = ocxl_config_set_TL ( pdev , fcfg - > dvsec_tl_pos ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_config_set_TL failed rc=%d \n " ,
__func__ , rc ) ;
goto err ;
}
2018-03-26 19:31:09 +03:00
out :
return rc ;
2018-03-26 19:33:28 +03:00
err :
ocxl_link_release ( pdev , afu - > link_token ) ;
goto out ;
2018-03-26 19:31:09 +03:00
}
2018-03-26 19:33:21 +03:00
/**
* ocxlflash_unconfig_fn ( ) - unconfigure the host function
* @ pdev : PCI device associated with the host .
* @ afu : AFU associated with the host .
*/
static void ocxlflash_unconfig_fn ( struct pci_dev * pdev , struct ocxl_hw_afu * afu )
{
ocxl_link_release ( pdev , afu - > link_token ) ;
}
2018-03-26 19:32:37 +03:00
/**
* ocxlflash_map_mmio ( ) - map the AFU MMIO space
* @ afu : AFU associated with the host .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_map_mmio ( struct ocxl_hw_afu * afu )
{
struct ocxl_afu_config * acfg = & afu - > acfg ;
struct pci_dev * pdev = afu - > pdev ;
struct device * dev = afu - > dev ;
phys_addr_t gmmio , ppmmio ;
int rc = 0 ;
rc = pci_request_region ( pdev , acfg - > global_mmio_bar , " ocxlflash " ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: pci_request_region for global failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
gmmio = pci_resource_start ( pdev , acfg - > global_mmio_bar ) ;
gmmio + = acfg - > global_mmio_offset ;
rc = pci_request_region ( pdev , acfg - > pp_mmio_bar , " ocxlflash " ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: pci_request_region for pp bar failed rc=%d \n " ,
__func__ , rc ) ;
goto err1 ;
}
ppmmio = pci_resource_start ( pdev , acfg - > pp_mmio_bar ) ;
ppmmio + = acfg - > pp_mmio_offset ;
afu - > gmmio_virt = ioremap ( gmmio , acfg - > global_mmio_size ) ;
if ( unlikely ( ! afu - > gmmio_virt ) ) {
dev_err ( dev , " %s: MMIO mapping failed \n " , __func__ ) ;
rc = - ENOMEM ;
goto err2 ;
}
afu - > gmmio_phys = gmmio ;
afu - > ppmmio_phys = ppmmio ;
out :
return rc ;
err2 :
pci_release_region ( pdev , acfg - > pp_mmio_bar ) ;
err1 :
pci_release_region ( pdev , acfg - > global_mmio_bar ) ;
goto out ;
}
2018-03-26 19:31:29 +03:00
/**
* ocxlflash_config_afu ( ) - configure the host AFU
* @ pdev : PCI device associated with the host .
* @ afu : AFU associated with the host .
*
* Must be called _after_ host function configuration .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_config_afu ( struct pci_dev * pdev , struct ocxl_hw_afu * afu )
{
struct ocxl_afu_config * acfg = & afu - > acfg ;
struct ocxl_fn_config * fcfg = & afu - > fcfg ;
struct device * dev = & pdev - > dev ;
2018-03-26 19:31:36 +03:00
int count ;
int base ;
int pos ;
2018-03-26 19:31:29 +03:00
int rc = 0 ;
/* This HW AFU function does not have any AFUs defined */
if ( ! afu - > is_present )
goto out ;
/* Read AFU config at index 0 */
rc = ocxl_config_read_afu ( pdev , fcfg , acfg , 0 ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxl_config_read_afu failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
2018-03-26 19:31:36 +03:00
/* Only one AFU per function is supported, so actag_base is same */
base = afu - > fn_actag_base ;
count = min_t ( int , acfg - > actag_supported , afu - > fn_actag_enabled ) ;
pos = acfg - > dvsec_afu_control_pos ;
ocxl_config_set_afu_actag ( pdev , pos , base , count ) ;
dev_dbg ( dev , " %s: acTag base=%d enabled=%d \n " , __func__ , base , count ) ;
afu - > afu_actag_base = base ;
afu - > afu_actag_enabled = count ;
2018-03-26 19:31:44 +03:00
afu - > max_pasid = 1 < < acfg - > pasid_supported_log ;
ocxl_config_set_afu_pasid ( pdev , pos , 0 , acfg - > pasid_supported_log ) ;
2018-03-26 19:32:37 +03:00
rc = ocxlflash_map_mmio ( afu ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: ocxlflash_map_mmio failed rc=%d \n " ,
__func__ , rc ) ;
goto out ;
}
2018-03-26 19:33:05 +03:00
/* Enable the AFU */
ocxl_config_set_afu_state ( pdev , acfg - > dvsec_afu_control_pos , 1 ) ;
2018-03-26 19:31:29 +03:00
out :
return rc ;
}
2018-03-26 19:31:01 +03:00
/**
* ocxlflash_create_afu ( ) - create the AFU for OCXL
* @ pdev : PCI device associated with the host .
*
* Return : AFU on success , NULL on failure
*/
static void * ocxlflash_create_afu ( struct pci_dev * pdev )
{
struct device * dev = & pdev - > dev ;
2018-03-26 19:31:53 +03:00
struct ocxlflash_context * ctx ;
2018-03-26 19:31:01 +03:00
struct ocxl_hw_afu * afu ;
2018-03-26 19:31:09 +03:00
int rc ;
2018-03-26 19:31:01 +03:00
afu = kzalloc ( sizeof ( * afu ) , GFP_KERNEL ) ;
if ( unlikely ( ! afu ) ) {
dev_err ( dev , " %s: HW AFU allocation failed \n " , __func__ ) ;
goto out ;
}
afu - > pdev = pdev ;
afu - > dev = dev ;
2018-03-26 19:32:00 +03:00
idr_init ( & afu - > idr ) ;
2018-03-26 19:31:09 +03:00
rc = ocxlflash_config_fn ( pdev , afu ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: Function configuration failed rc=%d \n " ,
__func__ , rc ) ;
goto err1 ;
}
2018-03-26 19:31:29 +03:00
rc = ocxlflash_config_afu ( pdev , afu ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: AFU configuration failed rc=%d \n " ,
__func__ , rc ) ;
2018-03-26 19:33:21 +03:00
goto err2 ;
2018-03-26 19:31:29 +03:00
}
2018-03-26 19:31:53 +03:00
ctx = ocxlflash_dev_context_init ( pdev , afu ) ;
if ( IS_ERR ( ctx ) ) {
rc = PTR_ERR ( ctx ) ;
dev_err ( dev , " %s: ocxlflash_dev_context_init failed rc=%d \n " ,
__func__ , rc ) ;
2018-03-26 19:33:21 +03:00
goto err3 ;
2018-03-26 19:31:53 +03:00
}
afu - > ocxl_ctx = ctx ;
2018-03-26 19:31:01 +03:00
out :
return afu ;
2018-03-26 19:33:21 +03:00
err3 :
2018-03-26 19:32:37 +03:00
ocxlflash_unconfig_afu ( afu ) ;
2018-03-26 19:33:21 +03:00
err2 :
ocxlflash_unconfig_fn ( pdev , afu ) ;
2018-03-26 19:31:09 +03:00
err1 :
2018-03-26 19:32:00 +03:00
idr_destroy ( & afu - > idr ) ;
2018-03-26 19:31:09 +03:00
kfree ( afu ) ;
afu = NULL ;
goto out ;
2018-03-26 19:31:01 +03:00
}
2018-03-26 19:30:51 +03:00
2018-03-26 19:34:03 +03:00
/**
* ctx_event_pending ( ) - check for any event pending on the context
* @ ctx : Context to be checked .
*
* Return : true if there is an event pending , false if none pending
*/
static inline bool ctx_event_pending ( struct ocxlflash_context * ctx )
{
2018-03-26 19:35:07 +03:00
if ( ctx - > pending_irq | | ctx - > pending_fault )
2018-03-26 19:34:03 +03:00
return true ;
return false ;
}
/**
* afu_poll ( ) - poll the AFU for events on the context
* @ file : File associated with the adapter context .
* @ poll : Poll structure from the user .
*
* Return : poll mask
*/
static unsigned int afu_poll ( struct file * file , struct poll_table_struct * poll )
{
struct ocxlflash_context * ctx = file - > private_data ;
struct device * dev = ctx - > hw_afu - > dev ;
ulong lock_flags ;
int mask = 0 ;
poll_wait ( file , & ctx - > wq , poll ) ;
spin_lock_irqsave ( & ctx - > slock , lock_flags ) ;
if ( ctx_event_pending ( ctx ) )
mask | = POLLIN | POLLRDNORM ;
2018-03-26 19:35:00 +03:00
else if ( ctx - > state = = CLOSED )
2018-03-26 19:34:03 +03:00
mask | = POLLERR ;
spin_unlock_irqrestore ( & ctx - > slock , lock_flags ) ;
dev_dbg ( dev , " %s: Poll wait completed for pe %i mask %i \n " ,
__func__ , ctx - > pe , mask ) ;
return mask ;
}
2018-03-26 19:34:12 +03:00
/**
* afu_read ( ) - perform a read on the context for any event
* @ file : File associated with the adapter context .
* @ buf : Buffer to receive the data .
* @ count : Size of buffer ( maximum bytes that can be read ) .
* @ off : Offset .
*
* Return : size of the data read on success , - errno on failure
*/
static ssize_t afu_read ( struct file * file , char __user * buf , size_t count ,
loff_t * off )
{
struct ocxlflash_context * ctx = file - > private_data ;
struct device * dev = ctx - > hw_afu - > dev ;
struct cxl_event event ;
ulong lock_flags ;
ssize_t esize ;
ssize_t rc ;
int bit ;
DEFINE_WAIT ( event_wait ) ;
if ( * off ! = 0 ) {
dev_err ( dev , " %s: Non-zero offset not supported, off=%lld \n " ,
__func__ , * off ) ;
rc = - EINVAL ;
goto out ;
}
spin_lock_irqsave ( & ctx - > slock , lock_flags ) ;
for ( ; ; ) {
prepare_to_wait ( & ctx - > wq , & event_wait , TASK_INTERRUPTIBLE ) ;
2018-03-26 19:35:00 +03:00
if ( ctx_event_pending ( ctx ) | | ( ctx - > state = = CLOSED ) )
2018-03-26 19:34:12 +03:00
break ;
if ( file - > f_flags & O_NONBLOCK ) {
dev_err ( dev , " %s: File cannot be blocked on I/O \n " ,
__func__ ) ;
rc = - EAGAIN ;
goto err ;
}
if ( signal_pending ( current ) ) {
dev_err ( dev , " %s: Signal pending on the process \n " ,
__func__ ) ;
rc = - ERESTARTSYS ;
goto err ;
}
spin_unlock_irqrestore ( & ctx - > slock , lock_flags ) ;
schedule ( ) ;
spin_lock_irqsave ( & ctx - > slock , lock_flags ) ;
}
finish_wait ( & ctx - > wq , & event_wait ) ;
memset ( & event , 0 , sizeof ( event ) ) ;
event . header . process_element = ctx - > pe ;
event . header . size = sizeof ( struct cxl_event_header ) ;
if ( ctx - > pending_irq ) {
esize = sizeof ( struct cxl_event_afu_interrupt ) ;
event . header . size + = esize ;
event . header . type = CXL_EVENT_AFU_INTERRUPT ;
bit = find_first_bit ( & ctx - > irq_bitmap , ctx - > num_irqs ) ;
clear_bit ( bit , & ctx - > irq_bitmap ) ;
event . irq . irq = bit + 1 ;
if ( bitmap_empty ( & ctx - > irq_bitmap , ctx - > num_irqs ) )
ctx - > pending_irq = false ;
2018-03-26 19:35:07 +03:00
} else if ( ctx - > pending_fault ) {
event . header . size + = sizeof ( struct cxl_event_data_storage ) ;
event . header . type = CXL_EVENT_DATA_STORAGE ;
event . fault . addr = ctx - > fault_addr ;
event . fault . dsisr = ctx - > fault_dsisr ;
ctx - > pending_fault = false ;
2018-03-26 19:34:12 +03:00
}
spin_unlock_irqrestore ( & ctx - > slock , lock_flags ) ;
if ( copy_to_user ( buf , & event , event . header . size ) ) {
dev_err ( dev , " %s: copy_to_user failed \n " , __func__ ) ;
rc = - EFAULT ;
goto out ;
}
rc = event . header . size ;
out :
return rc ;
err :
finish_wait ( & ctx - > wq , & event_wait ) ;
spin_unlock_irqrestore ( & ctx - > slock , lock_flags ) ;
goto out ;
}
2018-03-26 19:34:20 +03:00
/**
* afu_release ( ) - release and free the context
* @ inode : File inode pointer .
* @ file : File associated with the context .
*
* Return : 0 on success , - errno on failure
*/
static int afu_release ( struct inode * inode , struct file * file )
{
struct ocxlflash_context * ctx = file - > private_data ;
int i ;
/* Unmap and free the interrupts associated with the context */
for ( i = ctx - > num_irqs ; i > = 0 ; i - - )
afu_unmap_irq ( 0 , ctx , i , ctx ) ;
free_afu_irqs ( ctx ) ;
return ocxlflash_release_context ( ctx ) ;
}
/**
* ocxlflash_mmap_fault ( ) - mmap fault handler
* @ vmf : VM fault associated with current fault .
*
* Return : 0 on success , - errno on failure
*/
2018-06-20 21:25:07 +03:00
static vm_fault_t ocxlflash_mmap_fault ( struct vm_fault * vmf )
2018-03-26 19:34:20 +03:00
{
struct vm_area_struct * vma = vmf - > vma ;
struct ocxlflash_context * ctx = vma - > vm_file - > private_data ;
2018-03-26 19:35:00 +03:00
struct device * dev = ctx - > hw_afu - > dev ;
2018-03-26 19:34:20 +03:00
u64 mmio_area , offset ;
offset = vmf - > pgoff < < PAGE_SHIFT ;
if ( offset > = ctx - > psn_size )
return VM_FAULT_SIGBUS ;
2018-03-26 19:35:00 +03:00
mutex_lock ( & ctx - > state_mutex ) ;
if ( ctx - > state ! = STARTED ) {
dev_err ( dev , " %s: Context not started, state=%d \n " ,
__func__ , ctx - > state ) ;
mutex_unlock ( & ctx - > state_mutex ) ;
return VM_FAULT_SIGBUS ;
}
mutex_unlock ( & ctx - > state_mutex ) ;
2018-03-26 19:34:20 +03:00
mmio_area = ctx - > psn_phys ;
mmio_area + = offset ;
2018-06-20 21:25:07 +03:00
return vmf_insert_pfn ( vma , vmf - > address , mmio_area > > PAGE_SHIFT ) ;
2018-03-26 19:34:20 +03:00
}
static const struct vm_operations_struct ocxlflash_vmops = {
. fault = ocxlflash_mmap_fault ,
} ;
/**
* afu_mmap ( ) - map the fault handler operations
* @ file : File associated with the context .
* @ vma : VM area associated with mapping .
*
* Return : 0 on success , - errno on failure
*/
static int afu_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct ocxlflash_context * ctx = file - > private_data ;
if ( ( vma_pages ( vma ) + vma - > vm_pgoff ) >
( ctx - > psn_size > > PAGE_SHIFT ) )
return - EINVAL ;
vma - > vm_flags | = VM_IO | VM_PFNMAP ;
vma - > vm_page_prot = pgprot_noncached ( vma - > vm_page_prot ) ;
vma - > vm_ops = & ocxlflash_vmops ;
return 0 ;
}
2018-03-26 19:32:09 +03:00
static const struct file_operations ocxl_afu_fops = {
. owner = THIS_MODULE ,
2018-03-26 19:34:03 +03:00
. poll = afu_poll ,
2018-03-26 19:34:12 +03:00
. read = afu_read ,
2018-03-26 19:34:20 +03:00
. release = afu_release ,
. mmap = afu_mmap ,
2018-03-26 19:32:09 +03:00
} ;
2018-03-26 19:34:03 +03:00
# define PATCH_FOPS(NAME) \
do { if ( ! fops - > NAME ) fops - > NAME = ocxl_afu_fops . NAME ; } while ( 0 )
2018-03-26 19:32:09 +03:00
/**
* ocxlflash_get_fd ( ) - get file descriptor for an adapter context
* @ ctx_cookie : Adapter context .
* @ fops : File operations to be associated .
* @ fd : File descriptor to be returned back .
*
* Return : pointer to the file on success , ERR_PTR on failure
*/
static struct file * ocxlflash_get_fd ( void * ctx_cookie ,
struct file_operations * fops , int * fd )
{
struct ocxlflash_context * ctx = ctx_cookie ;
struct device * dev = ctx - > hw_afu - > dev ;
struct file * file ;
int flags , fdtmp ;
int rc = 0 ;
char * name = NULL ;
/* Only allow one fd per context */
if ( ctx - > mapping ) {
dev_err ( dev , " %s: Context is already mapped to an fd \n " ,
__func__ ) ;
rc = - EEXIST ;
goto err1 ;
}
flags = O_RDWR | O_CLOEXEC ;
/* This code is similar to anon_inode_getfd() */
rc = get_unused_fd_flags ( flags ) ;
if ( unlikely ( rc < 0 ) ) {
dev_err ( dev , " %s: get_unused_fd_flags failed rc=%d \n " ,
__func__ , rc ) ;
goto err1 ;
}
fdtmp = rc ;
2018-03-26 19:34:03 +03:00
/* Patch the file ops that are not defined */
if ( fops ) {
PATCH_FOPS ( poll ) ;
2018-03-26 19:34:12 +03:00
PATCH_FOPS ( read ) ;
2018-03-26 19:34:20 +03:00
PATCH_FOPS ( release ) ;
PATCH_FOPS ( mmap ) ;
2018-03-26 19:34:03 +03:00
} else /* Use default ops */
2018-03-26 19:32:09 +03:00
fops = ( struct file_operations * ) & ocxl_afu_fops ;
name = kasprintf ( GFP_KERNEL , " ocxlflash:%d " , ctx - > pe ) ;
file = ocxlflash_getfile ( dev , name , fops , ctx , flags ) ;
kfree ( name ) ;
if ( IS_ERR ( file ) ) {
rc = PTR_ERR ( file ) ;
dev_err ( dev , " %s: ocxlflash_getfile failed rc=%d \n " ,
__func__ , rc ) ;
goto err2 ;
}
ctx - > mapping = file - > f_mapping ;
* fd = fdtmp ;
out :
return file ;
err2 :
put_unused_fd ( fdtmp ) ;
err1 :
file = ERR_PTR ( rc ) ;
goto out ;
}
2018-03-26 19:32:20 +03:00
/**
* ocxlflash_fops_get_context ( ) - get the context associated with the file
* @ file : File associated with the adapter context .
*
* Return : pointer to the context
*/
static void * ocxlflash_fops_get_context ( struct file * file )
{
return file - > private_data ;
}
2018-03-26 19:33:55 +03:00
/**
* ocxlflash_afu_irq ( ) - interrupt handler for user contexts
* @ irq : Interrupt number .
* @ data : Private data provided at interrupt registration , the context .
*
* Return : Always return IRQ_HANDLED .
*/
static irqreturn_t ocxlflash_afu_irq ( int irq , void * data )
{
struct ocxlflash_context * ctx = data ;
struct device * dev = ctx - > hw_afu - > dev ;
int i ;
dev_dbg ( dev , " %s: Interrupt raised for pe %i virq %i \n " ,
__func__ , ctx - > pe , irq ) ;
for ( i = 0 ; i < ctx - > num_irqs ; i + + ) {
if ( ctx - > irqs [ i ] . virq = = irq )
break ;
}
if ( unlikely ( i > = ctx - > num_irqs ) ) {
dev_err ( dev , " %s: Received AFU IRQ out of range \n " , __func__ ) ;
goto out ;
}
spin_lock ( & ctx - > slock ) ;
set_bit ( i - 1 , & ctx - > irq_bitmap ) ;
ctx - > pending_irq = true ;
spin_unlock ( & ctx - > slock ) ;
2018-03-26 19:34:03 +03:00
wake_up_all ( & ctx - > wq ) ;
2018-03-26 19:33:55 +03:00
out :
return IRQ_HANDLED ;
}
/**
* ocxlflash_start_work ( ) - start a user context
* @ ctx_cookie : Context to be started .
* @ num_irqs : Number of interrupts requested .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_start_work ( void * ctx_cookie , u64 num_irqs )
{
struct ocxlflash_context * ctx = ctx_cookie ;
struct ocxl_hw_afu * afu = ctx - > hw_afu ;
struct device * dev = afu - > dev ;
char * name ;
int rc = 0 ;
int i ;
rc = alloc_afu_irqs ( ctx , num_irqs ) ;
if ( unlikely ( rc < 0 ) ) {
dev_err ( dev , " %s: alloc_afu_irqs failed rc=%d \n " , __func__ , rc ) ;
goto out ;
}
for ( i = 0 ; i < num_irqs ; i + + ) {
name = kasprintf ( GFP_KERNEL , " ocxlflash-%s-pe%i-%i " ,
dev_name ( dev ) , ctx - > pe , i ) ;
rc = afu_map_irq ( 0 , ctx , i , ocxlflash_afu_irq , ctx , name ) ;
kfree ( name ) ;
if ( unlikely ( rc < 0 ) ) {
dev_err ( dev , " %s: afu_map_irq failed rc=%d \n " ,
__func__ , rc ) ;
goto err ;
}
}
rc = start_context ( ctx ) ;
if ( unlikely ( rc ) ) {
dev_err ( dev , " %s: start_context failed rc=%d \n " , __func__ , rc ) ;
goto err ;
}
out :
return rc ;
err :
for ( i = i - 1 ; i > = 0 ; i - - )
afu_unmap_irq ( 0 , ctx , i , ctx ) ;
free_afu_irqs ( ctx ) ;
goto out ;
2018-03-26 19:34:27 +03:00
} ;
/**
* ocxlflash_fd_mmap ( ) - mmap handler for adapter file descriptor
* @ file : File installed with adapter file descriptor .
* @ vma : VM area associated with mapping .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_fd_mmap ( struct file * file , struct vm_area_struct * vma )
{
return afu_mmap ( file , vma ) ;
}
/**
* ocxlflash_fd_release ( ) - release the context associated with the file
* @ inode : File inode pointer .
* @ file : File associated with the adapter context .
*
* Return : 0 on success , - errno on failure
*/
static int ocxlflash_fd_release ( struct inode * inode , struct file * file )
{
return afu_release ( inode , file ) ;
2018-03-26 19:33:55 +03:00
}
2018-03-26 19:30:51 +03:00
/* Backend ops to ocxlflash services */
const struct cxlflash_backend_ops cxlflash_ocxl_ops = {
. module = THIS_MODULE ,
2018-03-26 19:32:56 +03:00
. psa_map = ocxlflash_psa_map ,
. psa_unmap = ocxlflash_psa_unmap ,
2018-03-26 19:32:20 +03:00
. process_element = ocxlflash_process_element ,
2018-03-26 19:33:48 +03:00
. map_afu_irq = ocxlflash_map_afu_irq ,
. unmap_afu_irq = ocxlflash_unmap_afu_irq ,
2018-03-26 19:34:35 +03:00
. get_irq_objhndl = ocxlflash_get_irq_objhndl ,
2018-03-26 19:32:48 +03:00
. start_context = ocxlflash_start_context ,
2018-03-26 19:33:35 +03:00
. stop_context = ocxlflash_stop_context ,
2018-03-26 19:35:15 +03:00
. afu_reset = ocxlflash_afu_reset ,
2018-03-26 19:31:53 +03:00
. set_master = ocxlflash_set_master ,
. get_context = ocxlflash_get_context ,
. dev_context_init = ocxlflash_dev_context_init ,
. release_context = ocxlflash_release_context ,
2018-03-26 19:32:29 +03:00
. perst_reloads_same_image = ocxlflash_perst_reloads_same_image ,
2018-03-26 19:33:14 +03:00
. read_adapter_vpd = ocxlflash_read_adapter_vpd ,
2018-03-26 19:33:41 +03:00
. allocate_afu_irqs = ocxlflash_allocate_afu_irqs ,
. free_afu_irqs = ocxlflash_free_afu_irqs ,
2018-03-26 19:31:01 +03:00
. create_afu = ocxlflash_create_afu ,
. destroy_afu = ocxlflash_destroy_afu ,
2018-03-26 19:32:09 +03:00
. get_fd = ocxlflash_get_fd ,
2018-03-26 19:32:20 +03:00
. fops_get_context = ocxlflash_fops_get_context ,
2018-03-26 19:33:55 +03:00
. start_work = ocxlflash_start_work ,
2018-03-26 19:34:27 +03:00
. fd_mmap = ocxlflash_fd_mmap ,
. fd_release = ocxlflash_fd_release ,
2018-03-26 19:30:51 +03:00
} ;