2019-03-27 16:31:30 +11:00
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2019 IBM Corp.
# include <linux/idr.h>
# include "ocxl_internal.h"
static struct ocxl_fn * ocxl_fn_get ( struct ocxl_fn * fn )
{
return ( get_device ( & fn - > dev ) = = NULL ) ? NULL : fn ;
}
static void ocxl_fn_put ( struct ocxl_fn * fn )
{
put_device ( & fn - > dev ) ;
}
static struct ocxl_afu * alloc_afu ( struct ocxl_fn * fn )
{
struct ocxl_afu * afu ;
afu = kzalloc ( sizeof ( struct ocxl_afu ) , GFP_KERNEL ) ;
if ( ! afu )
return NULL ;
2019-03-27 16:31:32 +11:00
kref_init ( & afu - > kref ) ;
2019-03-27 16:31:30 +11:00
mutex_init ( & afu - > contexts_lock ) ;
mutex_init ( & afu - > afu_control_lock ) ;
idr_init ( & afu - > contexts_idr ) ;
afu - > fn = fn ;
ocxl_fn_get ( fn ) ;
return afu ;
}
2019-03-27 16:31:32 +11:00
static void free_afu ( struct kref * kref )
2019-03-27 16:31:30 +11:00
{
2019-03-27 16:31:32 +11:00
struct ocxl_afu * afu = container_of ( kref , struct ocxl_afu , kref ) ;
2019-03-27 16:31:30 +11:00
idr_destroy ( & afu - > contexts_idr ) ;
ocxl_fn_put ( afu - > fn ) ;
kfree ( afu ) ;
}
2019-03-27 16:31:32 +11:00
void ocxl_afu_get ( struct ocxl_afu * afu )
2019-03-27 16:31:30 +11:00
{
2019-03-27 16:31:32 +11:00
kref_get ( & afu - > kref ) ;
2019-03-27 16:31:30 +11:00
}
2019-03-27 16:31:32 +11:00
EXPORT_SYMBOL_GPL ( ocxl_afu_get ) ;
2019-03-27 16:31:30 +11:00
2019-03-27 16:31:32 +11:00
void ocxl_afu_put ( struct ocxl_afu * afu )
2019-03-27 16:31:30 +11:00
{
2019-03-27 16:31:32 +11:00
kref_put ( & afu - > kref , free_afu ) ;
2019-03-27 16:31:30 +11:00
}
2019-03-27 16:31:32 +11:00
EXPORT_SYMBOL_GPL ( ocxl_afu_put ) ;
2019-03-27 16:31:30 +11:00
2019-03-27 16:31:31 +11:00
static int assign_afu_actag ( struct ocxl_afu * afu )
2019-03-27 16:31:30 +11:00
{
struct ocxl_fn * fn = afu - > fn ;
int actag_count , actag_offset ;
2019-03-27 16:31:31 +11:00
struct pci_dev * pci_dev = to_pci_dev ( fn - > dev . parent ) ;
2019-03-27 16:31:30 +11:00
/*
* if there were not enough actags for the function , each afu
* reduces its count as well
*/
actag_count = afu - > config . actag_supported *
fn - > actag_enabled / fn - > actag_supported ;
actag_offset = ocxl_actag_afu_alloc ( fn , actag_count ) ;
if ( actag_offset < 0 ) {
2019-03-27 16:31:31 +11:00
dev_err ( & pci_dev - > dev , " Can't allocate %d actags for AFU: %d \n " ,
2019-03-27 16:31:30 +11:00
actag_count , actag_offset ) ;
return actag_offset ;
}
afu - > actag_base = fn - > actag_base + actag_offset ;
afu - > actag_enabled = actag_count ;
2019-03-27 16:31:31 +11:00
ocxl_config_set_afu_actag ( pci_dev , afu - > config . dvsec_afu_control_pos ,
2019-03-27 16:31:30 +11:00
afu - > actag_base , afu - > actag_enabled ) ;
2019-03-27 16:31:31 +11:00
dev_dbg ( & pci_dev - > dev , " actag base=%d enabled=%d \n " ,
2019-03-27 16:31:30 +11:00
afu - > actag_base , afu - > actag_enabled ) ;
return 0 ;
}
static void reclaim_afu_actag ( struct ocxl_afu * afu )
{
struct ocxl_fn * fn = afu - > fn ;
int start_offset , size ;
start_offset = afu - > actag_base - fn - > actag_base ;
size = afu - > actag_enabled ;
ocxl_actag_afu_free ( afu - > fn , start_offset , size ) ;
}
2019-03-27 16:31:31 +11:00
static int assign_afu_pasid ( struct ocxl_afu * afu )
2019-03-27 16:31:30 +11:00
{
struct ocxl_fn * fn = afu - > fn ;
int pasid_count , pasid_offset ;
2019-03-27 16:31:31 +11:00
struct pci_dev * pci_dev = to_pci_dev ( fn - > dev . parent ) ;
2019-03-27 16:31:30 +11:00
/*
* We only support the case where the function configuration
* requested enough PASIDs to cover all AFUs .
*/
pasid_count = 1 < < afu - > config . pasid_supported_log ;
pasid_offset = ocxl_pasid_afu_alloc ( fn , pasid_count ) ;
if ( pasid_offset < 0 ) {
2019-03-27 16:31:31 +11:00
dev_err ( & pci_dev - > dev , " Can't allocate %d PASIDs for AFU: %d \n " ,
2019-03-27 16:31:30 +11:00
pasid_count , pasid_offset ) ;
return pasid_offset ;
}
afu - > pasid_base = fn - > pasid_base + pasid_offset ;
afu - > pasid_count = 0 ;
afu - > pasid_max = pasid_count ;
2019-03-27 16:31:31 +11:00
ocxl_config_set_afu_pasid ( pci_dev , afu - > config . dvsec_afu_control_pos ,
2019-03-27 16:31:30 +11:00
afu - > pasid_base ,
afu - > config . pasid_supported_log ) ;
2019-03-27 16:31:31 +11:00
dev_dbg ( & pci_dev - > dev , " PASID base=%d, enabled=%d \n " ,
2019-03-27 16:31:30 +11:00
afu - > pasid_base , pasid_count ) ;
return 0 ;
}
static void reclaim_afu_pasid ( struct ocxl_afu * afu )
{
struct ocxl_fn * fn = afu - > fn ;
int start_offset , size ;
start_offset = afu - > pasid_base - fn - > pasid_base ;
size = 1 < < afu - > config . pasid_supported_log ;
ocxl_pasid_afu_free ( afu - > fn , start_offset , size ) ;
}
static int reserve_fn_bar ( struct ocxl_fn * fn , int bar )
{
struct pci_dev * dev = to_pci_dev ( fn - > dev . parent ) ;
int rc , idx ;
if ( bar ! = 0 & & bar ! = 2 & & bar ! = 4 )
return - EINVAL ;
idx = bar > > 1 ;
if ( fn - > bar_used [ idx ] + + = = 0 ) {
rc = pci_request_region ( dev , bar , " ocxl " ) ;
if ( rc )
return rc ;
}
return 0 ;
}
static void release_fn_bar ( struct ocxl_fn * fn , int bar )
{
struct pci_dev * dev = to_pci_dev ( fn - > dev . parent ) ;
int idx ;
if ( bar ! = 0 & & bar ! = 2 & & bar ! = 4 )
return ;
idx = bar > > 1 ;
if ( - - fn - > bar_used [ idx ] = = 0 )
pci_release_region ( dev , bar ) ;
WARN_ON ( fn - > bar_used [ idx ] < 0 ) ;
}
2019-03-27 16:31:31 +11:00
static int map_mmio_areas ( struct ocxl_afu * afu )
2019-03-27 16:31:30 +11:00
{
int rc ;
2019-03-27 16:31:31 +11:00
struct pci_dev * pci_dev = to_pci_dev ( afu - > fn - > dev . parent ) ;
2019-03-27 16:31:30 +11:00
rc = reserve_fn_bar ( afu - > fn , afu - > config . global_mmio_bar ) ;
if ( rc )
return rc ;
rc = reserve_fn_bar ( afu - > fn , afu - > config . pp_mmio_bar ) ;
if ( rc ) {
release_fn_bar ( afu - > fn , afu - > config . global_mmio_bar ) ;
return rc ;
}
afu - > global_mmio_start =
2019-03-27 16:31:31 +11:00
pci_resource_start ( pci_dev , afu - > config . global_mmio_bar ) +
2019-03-27 16:31:30 +11:00
afu - > config . global_mmio_offset ;
afu - > pp_mmio_start =
2019-03-27 16:31:31 +11:00
pci_resource_start ( pci_dev , afu - > config . pp_mmio_bar ) +
2019-03-27 16:31:30 +11:00
afu - > config . pp_mmio_offset ;
afu - > global_mmio_ptr = ioremap ( afu - > global_mmio_start ,
afu - > config . global_mmio_size ) ;
if ( ! afu - > global_mmio_ptr ) {
release_fn_bar ( afu - > fn , afu - > config . pp_mmio_bar ) ;
release_fn_bar ( afu - > fn , afu - > config . global_mmio_bar ) ;
2019-03-27 16:31:31 +11:00
dev_err ( & pci_dev - > dev , " Error mapping global mmio area \n " ) ;
2019-03-27 16:31:30 +11:00
return - ENOMEM ;
}
/*
* Leave an empty page between the per - process mmio area and
* the AFU interrupt mappings
*/
afu - > irq_base_offset = afu - > config . pp_mmio_stride + PAGE_SIZE ;
return 0 ;
}
static void unmap_mmio_areas ( struct ocxl_afu * afu )
{
if ( afu - > global_mmio_ptr ) {
iounmap ( afu - > global_mmio_ptr ) ;
afu - > global_mmio_ptr = NULL ;
}
afu - > global_mmio_start = 0 ;
afu - > pp_mmio_start = 0 ;
release_fn_bar ( afu - > fn , afu - > config . pp_mmio_bar ) ;
release_fn_bar ( afu - > fn , afu - > config . global_mmio_bar ) ;
}
static int configure_afu ( struct ocxl_afu * afu , u8 afu_idx , struct pci_dev * dev )
{
int rc ;
rc = ocxl_config_read_afu ( dev , & afu - > fn - > config , & afu - > config , afu_idx ) ;
if ( rc )
return rc ;
2019-03-27 16:31:31 +11:00
rc = assign_afu_actag ( afu ) ;
2019-03-27 16:31:30 +11:00
if ( rc )
return rc ;
2019-03-27 16:31:31 +11:00
rc = assign_afu_pasid ( afu ) ;
2019-03-27 16:31:32 +11:00
if ( rc )
goto err_free_actag ;
2019-03-27 16:31:30 +11:00
2019-03-27 16:31:31 +11:00
rc = map_mmio_areas ( afu ) ;
2019-03-27 16:31:32 +11:00
if ( rc )
goto err_free_pasid ;
2019-03-27 16:31:30 +11:00
return 0 ;
2019-03-27 16:31:32 +11:00
err_free_pasid :
reclaim_afu_pasid ( afu ) ;
err_free_actag :
reclaim_afu_actag ( afu ) ;
return rc ;
2019-03-27 16:31:30 +11:00
}
static void deconfigure_afu ( struct ocxl_afu * afu )
{
unmap_mmio_areas ( afu ) ;
reclaim_afu_pasid ( afu ) ;
reclaim_afu_actag ( afu ) ;
}
static int activate_afu ( struct pci_dev * dev , struct ocxl_afu * afu )
{
ocxl_config_set_afu_state ( dev , afu - > config . dvsec_afu_control_pos , 1 ) ;
2019-03-27 16:31:32 +11:00
2019-03-27 16:31:30 +11:00
return 0 ;
}
static void deactivate_afu ( struct ocxl_afu * afu )
{
struct pci_dev * dev = to_pci_dev ( afu - > fn - > dev . parent ) ;
ocxl_config_set_afu_state ( dev , afu - > config . dvsec_afu_control_pos , 0 ) ;
}
2019-03-27 16:31:32 +11:00
static int init_afu ( struct pci_dev * dev , struct ocxl_fn * fn , u8 afu_idx )
2019-03-27 16:31:30 +11:00
{
int rc ;
struct ocxl_afu * afu ;
afu = alloc_afu ( fn ) ;
if ( ! afu )
return - ENOMEM ;
rc = configure_afu ( afu , afu_idx , dev ) ;
if ( rc ) {
2019-03-27 16:31:32 +11:00
ocxl_afu_put ( afu ) ;
2019-03-27 16:31:30 +11:00
return rc ;
}
rc = activate_afu ( dev , afu ) ;
2019-03-27 16:31:32 +11:00
if ( rc ) {
deconfigure_afu ( afu ) ;
ocxl_afu_put ( afu ) ;
return rc ;
}
2019-03-27 16:31:30 +11:00
list_add_tail ( & afu - > list , & fn - > afu_list ) ;
2019-03-27 16:31:32 +11:00
return 0 ;
2019-03-27 16:31:30 +11:00
}
2019-03-27 16:31:32 +11:00
static void remove_afu ( struct ocxl_afu * afu )
2019-03-27 16:31:30 +11:00
{
list_del ( & afu - > list ) ;
ocxl_context_detach_all ( afu ) ;
deactivate_afu ( afu ) ;
deconfigure_afu ( afu ) ;
2019-03-27 16:31:32 +11:00
ocxl_afu_put ( afu ) ; // matches the implicit get in alloc_afu
2019-03-27 16:31:30 +11:00
}
2019-03-27 16:31:31 +11:00
static struct ocxl_fn * alloc_function ( void )
2019-03-27 16:31:30 +11:00
{
struct ocxl_fn * fn ;
fn = kzalloc ( sizeof ( struct ocxl_fn ) , GFP_KERNEL ) ;
if ( ! fn )
return NULL ;
INIT_LIST_HEAD ( & fn - > afu_list ) ;
INIT_LIST_HEAD ( & fn - > pasid_list ) ;
INIT_LIST_HEAD ( & fn - > actag_list ) ;
2019-03-27 16:31:31 +11:00
2019-03-27 16:31:30 +11:00
return fn ;
}
static void free_function ( struct ocxl_fn * fn )
{
WARN_ON ( ! list_empty ( & fn - > afu_list ) ) ;
WARN_ON ( ! list_empty ( & fn - > pasid_list ) ) ;
kfree ( fn ) ;
}
static void free_function_dev ( struct device * dev )
{
2019-03-27 16:31:32 +11:00
struct ocxl_fn * fn = container_of ( dev , struct ocxl_fn , dev ) ;
2019-03-27 16:31:30 +11:00
free_function ( fn ) ;
}
static int set_function_device ( struct ocxl_fn * fn , struct pci_dev * dev )
{
int rc ;
fn - > dev . parent = & dev - > dev ;
fn - > dev . release = free_function_dev ;
rc = dev_set_name ( & fn - > dev , " ocxlfn.%s " , dev_name ( & dev - > dev ) ) ;
if ( rc )
return rc ;
return 0 ;
}
static int assign_function_actag ( struct ocxl_fn * fn )
{
struct pci_dev * dev = to_pci_dev ( fn - > dev . parent ) ;
u16 base , enabled , supported ;
int rc ;
rc = ocxl_config_get_actag_info ( dev , & base , & enabled , & supported ) ;
if ( rc )
return rc ;
fn - > actag_base = base ;
fn - > actag_enabled = enabled ;
fn - > actag_supported = supported ;
ocxl_config_set_actag ( dev , fn - > config . dvsec_function_pos ,
fn - > actag_base , fn - > actag_enabled ) ;
dev_dbg ( & fn - > dev , " actag range starting at %d, enabled %d \n " ,
fn - > actag_base , fn - > actag_enabled ) ;
return 0 ;
}
static int set_function_pasid ( struct ocxl_fn * fn )
{
struct pci_dev * dev = to_pci_dev ( fn - > dev . parent ) ;
int rc , desired_count , max_count ;
/* A function may not require any PASID */
if ( fn - > config . max_pasid_log < 0 )
return 0 ;
rc = ocxl_config_get_pasid_info ( dev , & max_count ) ;
if ( rc )
return rc ;
desired_count = 1 < < fn - > config . max_pasid_log ;
if ( desired_count > max_count ) {
dev_err ( & fn - > dev ,
" Function requires more PASIDs than is available (%d vs. %d) \n " ,
desired_count , max_count ) ;
return - ENOSPC ;
}
fn - > pasid_base = 0 ;
return 0 ;
}
static int configure_function ( struct ocxl_fn * fn , struct pci_dev * dev )
{
int rc ;
rc = pci_enable_device ( dev ) ;
if ( rc ) {
dev_err ( & dev - > dev , " pci_enable_device failed: %d \n " , rc ) ;
return rc ;
}
/*
* Once it has been confirmed to work on our hardware , we
* should reset the function , to force the adapter to restart
* from scratch .
* A function reset would also reset all its AFUs .
*
* Some hints for implementation :
*
* - there ' s not status bit to know when the reset is done . We
* should try reading the config space to know when it ' s
* done .
* - probably something like :
* Reset
* wait 100 ms
* issue config read
* allow device up to 1 sec to return success on config
* read before declaring it broken
*
* Some shared logic on the card ( CFG , TLX ) won ' t be reset , so
* there ' s no guarantee that it will be enough .
*/
rc = ocxl_config_read_function ( dev , & fn - > config ) ;
if ( rc )
return rc ;
rc = set_function_device ( fn , dev ) ;
if ( rc )
return rc ;
rc = assign_function_actag ( fn ) ;
if ( rc )
return rc ;
rc = set_function_pasid ( fn ) ;
if ( rc )
return rc ;
rc = ocxl_link_setup ( dev , 0 , & fn - > link ) ;
if ( rc )
return rc ;
rc = ocxl_config_set_TL ( dev , fn - > config . dvsec_tl_pos ) ;
if ( rc ) {
ocxl_link_release ( dev , fn - > link ) ;
return rc ;
}
return 0 ;
}
static void deconfigure_function ( struct ocxl_fn * fn )
{
struct pci_dev * dev = to_pci_dev ( fn - > dev . parent ) ;
ocxl_link_release ( dev , fn - > link ) ;
pci_disable_device ( dev ) ;
}
2019-03-27 16:31:32 +11:00
static struct ocxl_fn * init_function ( struct pci_dev * dev )
2019-03-27 16:31:30 +11:00
{
struct ocxl_fn * fn ;
int rc ;
2019-03-27 16:31:31 +11:00
fn = alloc_function ( ) ;
2019-03-27 16:31:30 +11:00
if ( ! fn )
return ERR_PTR ( - ENOMEM ) ;
rc = configure_function ( fn , dev ) ;
if ( rc ) {
free_function ( fn ) ;
return ERR_PTR ( rc ) ;
}
rc = device_register ( & fn - > dev ) ;
if ( rc ) {
deconfigure_function ( fn ) ;
put_device ( & fn - > dev ) ;
return ERR_PTR ( rc ) ;
}
return fn ;
}
2019-03-27 16:31:32 +11:00
// Device detection & initialisation
struct ocxl_fn * ocxl_function_open ( struct pci_dev * dev )
{
int rc , afu_count = 0 ;
u8 afu ;
struct ocxl_fn * fn ;
if ( ! radix_enabled ( ) ) {
dev_err ( & dev - > dev , " Unsupported memory model (hash) \n " ) ;
return ERR_PTR ( - ENODEV ) ;
}
fn = init_function ( dev ) ;
if ( IS_ERR ( fn ) ) {
dev_err ( & dev - > dev , " function init failed: %li \n " ,
PTR_ERR ( fn ) ) ;
return fn ;
}
for ( afu = 0 ; afu < = fn - > config . max_afu_index ; afu + + ) {
rc = ocxl_config_check_afu_index ( dev , & fn - > config , afu ) ;
if ( rc > 0 ) {
rc = init_afu ( dev , fn , afu ) ;
if ( rc ) {
dev_err ( & dev - > dev ,
" Can't initialize AFU index %d \n " , afu ) ;
continue ;
}
afu_count + + ;
}
}
dev_info ( & dev - > dev , " %d AFU(s) configured \n " , afu_count ) ;
return fn ;
}
EXPORT_SYMBOL_GPL ( ocxl_function_open ) ;
struct list_head * ocxl_function_afu_list ( struct ocxl_fn * fn )
{
return & fn - > afu_list ;
}
EXPORT_SYMBOL_GPL ( ocxl_function_afu_list ) ;
struct ocxl_afu * ocxl_function_fetch_afu ( struct ocxl_fn * fn , u8 afu_idx )
{
struct ocxl_afu * afu ;
list_for_each_entry ( afu , & fn - > afu_list , list ) {
if ( afu - > config . idx = = afu_idx )
return afu ;
}
return NULL ;
}
EXPORT_SYMBOL_GPL ( ocxl_function_fetch_afu ) ;
const struct ocxl_fn_config * ocxl_function_config ( struct ocxl_fn * fn )
2019-03-27 16:31:30 +11:00
{
2019-03-27 16:31:32 +11:00
return & fn - > config ;
}
EXPORT_SYMBOL_GPL ( ocxl_function_config ) ;
void ocxl_function_close ( struct ocxl_fn * fn )
{
struct ocxl_afu * afu , * tmp ;
list_for_each_entry_safe ( afu , tmp , & fn - > afu_list , list ) {
remove_afu ( afu ) ;
}
2019-03-27 16:31:30 +11:00
deconfigure_function ( fn ) ;
device_unregister ( & fn - > dev ) ;
}
2019-03-27 16:31:32 +11:00
EXPORT_SYMBOL_GPL ( ocxl_function_close ) ;
// AFU Metadata
struct ocxl_afu_config * ocxl_afu_config ( struct ocxl_afu * afu )
{
return & afu - > config ;
}
EXPORT_SYMBOL_GPL ( ocxl_afu_config ) ;
void ocxl_afu_set_private ( struct ocxl_afu * afu , void * private )
{
afu - > private = private ;
}
EXPORT_SYMBOL_GPL ( ocxl_afu_set_private ) ;
void * ocxl_afu_get_private ( struct ocxl_afu * afu )
{
if ( afu )
return afu - > private ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( ocxl_afu_get_private ) ;