2018-05-17 02:49:58 +03:00
// SPDX-License-Identifier: GPL-2.0
2016-11-01 22:14:29 +03:00
/*
2020-09-05 06:24:07 +03:00
* FPGA Region - Support for FPGA programming under Linux
2016-11-01 22:14:29 +03:00
*
* Copyright ( C ) 2013 - 2016 Altera Corporation
2017-11-15 23:20:25 +03:00
* Copyright ( C ) 2017 Intel Corporation
2016-11-01 22:14:29 +03:00
*/
# include <linux/fpga/fpga-bridge.h>
# include <linux/fpga/fpga-mgr.h>
2017-11-15 23:20:21 +03:00
# include <linux/fpga/fpga-region.h>
2016-11-01 22:14:29 +03:00
# include <linux/idr.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
static DEFINE_IDA ( fpga_region_ida ) ;
2023-08-11 10:30:43 +03:00
static const struct class fpga_region_class ;
2016-11-01 22:14:29 +03:00
2022-04-23 20:02:32 +03:00
struct fpga_region *
fpga_region_class_find ( struct device * start , const void * data ,
int ( * match ) ( struct device * , const void * ) )
2017-11-15 23:20:24 +03:00
{
struct device * dev ;
2023-08-11 10:30:43 +03:00
dev = class_find_device ( & fpga_region_class , start , data , match ) ;
2017-11-15 23:20:24 +03:00
if ( ! dev )
return NULL ;
return to_fpga_region ( dev ) ;
}
EXPORT_SYMBOL_GPL ( fpga_region_class_find ) ;
2016-11-01 22:14:29 +03:00
/**
2021-06-09 00:23:47 +03:00
* fpga_region_get - get an exclusive reference to an fpga region
2016-11-01 22:14:29 +03:00
* @ region : FPGA Region struct
*
* Caller should call fpga_region_put ( ) when done with region .
*
2023-07-06 17:27:54 +03:00
* Return :
* * fpga_region struct if successful .
* * - EBUSY if someone already has a reference to the region .
* * - ENODEV if can ' t take parent driver module refcount .
2016-11-01 22:14:29 +03:00
*/
static struct fpga_region * fpga_region_get ( struct fpga_region * region )
{
struct device * dev = & region - > dev ;
if ( ! mutex_trylock ( & region - > mutex ) ) {
dev_dbg ( dev , " %s: FPGA Region already in use \n " , __func__ ) ;
return ERR_PTR ( - EBUSY ) ;
}
get_device ( dev ) ;
if ( ! try_module_get ( dev - > parent - > driver - > owner ) ) {
put_device ( dev ) ;
mutex_unlock ( & region - > mutex ) ;
return ERR_PTR ( - ENODEV ) ;
}
2017-11-15 23:20:14 +03:00
dev_dbg ( dev , " get \n " ) ;
2016-11-01 22:14:29 +03:00
return region ;
}
/**
* fpga_region_put - release a reference to a region
*
* @ region : FPGA region
*/
static void fpga_region_put ( struct fpga_region * region )
{
struct device * dev = & region - > dev ;
2017-11-15 23:20:14 +03:00
dev_dbg ( dev , " put \n " ) ;
2016-11-01 22:14:29 +03:00
module_put ( dev - > parent - > driver - > owner ) ;
put_device ( dev ) ;
mutex_unlock ( & region - > mutex ) ;
}
/**
* fpga_region_program_fpga - program FPGA
2018-05-17 02:50:01 +03:00
*
2016-11-01 22:14:29 +03:00
* @ region : FPGA region
2018-05-17 02:50:01 +03:00
*
2017-11-15 23:20:19 +03:00
* Program an FPGA using fpga image info ( region - > info ) .
2018-04-17 06:43:37 +03:00
* If the region has a get_bridges function , the exclusive reference for the
* bridges will be held if programming succeeds . This is intended to prevent
* reprogramming the region until the caller considers it safe to do so .
* The caller will need to call fpga_bridges_put ( ) before attempting to
* reprogram the region .
2018-05-17 02:50:01 +03:00
*
2023-07-06 17:27:54 +03:00
* Return : 0 for success or negative error code .
2016-11-01 22:14:29 +03:00
*/
2017-11-15 23:20:21 +03:00
int fpga_region_program_fpga ( struct fpga_region * region )
2016-11-01 22:14:29 +03:00
{
2017-11-15 23:20:13 +03:00
struct device * dev = & region - > dev ;
2017-11-15 23:20:19 +03:00
struct fpga_image_info * info = region - > info ;
2016-11-01 22:14:29 +03:00
int ret ;
region = fpga_region_get ( region ) ;
if ( IS_ERR ( region ) ) {
2017-11-15 23:20:14 +03:00
dev_err ( dev , " failed to get FPGA region \n " ) ;
2016-11-01 22:14:29 +03:00
return PTR_ERR ( region ) ;
}
2017-11-15 23:20:16 +03:00
ret = fpga_mgr_lock ( region - > mgr ) ;
2017-11-15 23:20:13 +03:00
if ( ret ) {
dev_err ( dev , " FPGA manager is busy \n " ) ;
2017-11-15 23:20:16 +03:00
goto err_put_region ;
2017-11-15 23:20:13 +03:00
}
2017-11-15 23:20:23 +03:00
/*
* In some cases , we already have a list of bridges in the
* fpga region struct . Or we don ' t have any bridges .
*/
if ( region - > get_bridges ) {
ret = region - > get_bridges ( region ) ;
if ( ret ) {
dev_err ( dev , " failed to get fpga region bridges \n " ) ;
goto err_unlock_mgr ;
}
2016-11-01 22:14:29 +03:00
}
ret = fpga_bridges_disable ( & region - > bridge_list ) ;
if ( ret ) {
2017-11-15 23:20:14 +03:00
dev_err ( dev , " failed to disable bridges \n " ) ;
2016-11-01 22:14:29 +03:00
goto err_put_br ;
}
2017-11-15 23:20:19 +03:00
ret = fpga_mgr_load ( region - > mgr , info ) ;
2016-11-01 22:14:29 +03:00
if ( ret ) {
2017-11-15 23:20:14 +03:00
dev_err ( dev , " failed to load FPGA image \n " ) ;
2016-11-01 22:14:29 +03:00
goto err_put_br ;
}
ret = fpga_bridges_enable ( & region - > bridge_list ) ;
if ( ret ) {
2017-11-15 23:20:14 +03:00
dev_err ( dev , " failed to enable region bridges \n " ) ;
2016-11-01 22:14:29 +03:00
goto err_put_br ;
}
2017-11-15 23:20:16 +03:00
fpga_mgr_unlock ( region - > mgr ) ;
2016-11-01 22:14:29 +03:00
fpga_region_put ( region ) ;
return 0 ;
err_put_br :
2017-11-15 23:20:23 +03:00
if ( region - > get_bridges )
fpga_bridges_put ( & region - > bridge_list ) ;
2017-11-15 23:20:13 +03:00
err_unlock_mgr :
2017-11-15 23:20:16 +03:00
fpga_mgr_unlock ( region - > mgr ) ;
2017-04-25 00:34:21 +03:00
err_put_region :
2016-11-01 22:14:29 +03:00
fpga_region_put ( region ) ;
return ret ;
}
2017-11-15 23:20:21 +03:00
EXPORT_SYMBOL_GPL ( fpga_region_program_fpga ) ;
2016-11-01 22:14:29 +03:00
2018-06-30 03:53:12 +03:00
static ssize_t compat_id_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct fpga_region * region = to_fpga_region ( dev ) ;
if ( ! region - > compat_id )
return - ENOENT ;
return sprintf ( buf , " %016llx%016llx \n " ,
( unsigned long long ) region - > compat_id - > id_h ,
( unsigned long long ) region - > compat_id - > id_l ) ;
}
static DEVICE_ATTR_RO ( compat_id ) ;
static struct attribute * fpga_region_attrs [ ] = {
& dev_attr_compat_id . attr ,
NULL ,
} ;
ATTRIBUTE_GROUPS ( fpga_region ) ;
2018-05-17 02:49:57 +03:00
/**
2021-11-19 04:55:53 +03:00
* fpga_region_register_full - create and register an FPGA Region device
2021-06-14 20:09:06 +03:00
* @ parent : device parent
2021-11-19 04:55:53 +03:00
* @ info : parameters for FPGA Region
2018-10-16 01:20:03 +03:00
*
2021-11-19 04:55:53 +03:00
* Return : struct fpga_region or ERR_PTR ( )
2018-05-17 02:49:57 +03:00
*/
2021-11-19 04:55:53 +03:00
struct fpga_region *
fpga_region_register_full ( struct device * parent , const struct fpga_region_info * info )
2016-11-01 22:14:29 +03:00
{
2018-05-17 02:49:57 +03:00
struct fpga_region * region ;
2016-11-01 22:14:29 +03:00
int id , ret = 0 ;
2021-11-19 04:55:53 +03:00
if ( ! info ) {
dev_err ( parent ,
" Attempt to register without required info structure \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
2018-05-17 02:49:57 +03:00
region = kzalloc ( sizeof ( * region ) , GFP_KERNEL ) ;
if ( ! region )
2021-11-19 04:55:53 +03:00
return ERR_PTR ( - ENOMEM ) ;
2018-05-17 02:49:57 +03:00
2022-05-27 11:59:15 +03:00
id = ida_alloc ( & fpga_region_ida , GFP_KERNEL ) ;
2021-11-19 04:55:53 +03:00
if ( id < 0 ) {
ret = id ;
2018-05-17 02:49:57 +03:00
goto err_free ;
2021-11-19 04:55:53 +03:00
}
region - > mgr = info - > mgr ;
region - > compat_id = info - > compat_id ;
region - > priv = info - > priv ;
region - > get_bridges = info - > get_bridges ;
2016-11-01 22:14:29 +03:00
mutex_init ( & region - > mutex ) ;
INIT_LIST_HEAD ( & region - > bridge_list ) ;
2018-05-17 02:49:57 +03:00
2023-08-11 10:30:43 +03:00
region - > dev . class = & fpga_region_class ;
2021-06-14 20:09:06 +03:00
region - > dev . parent = parent ;
region - > dev . of_node = parent - > of_node ;
2016-11-01 22:14:29 +03:00
region - > dev . id = id ;
ret = dev_set_name ( & region - > dev , " region%d " , id ) ;
if ( ret )
goto err_remove ;
2021-11-19 04:55:53 +03:00
ret = device_register ( & region - > dev ) ;
if ( ret ) {
put_device ( & region - > dev ) ;
return ERR_PTR ( ret ) ;
}
2018-05-17 02:49:57 +03:00
return region ;
2017-11-15 23:20:23 +03:00
err_remove :
2022-05-27 11:59:15 +03:00
ida_free ( & fpga_region_ida , id ) ;
2018-05-17 02:49:57 +03:00
err_free :
kfree ( region ) ;
2021-11-19 04:55:53 +03:00
return ERR_PTR ( ret ) ;
2018-10-16 01:20:03 +03:00
}
2021-11-19 04:55:53 +03:00
EXPORT_SYMBOL_GPL ( fpga_region_register_full ) ;
2018-10-16 01:20:03 +03:00
/**
2021-11-19 04:55:53 +03:00
* fpga_region_register - create and register an FPGA Region device
2021-06-14 20:09:06 +03:00
* @ parent : device parent
2018-10-16 01:20:03 +03:00
* @ mgr : manager that programs this region
* @ get_bridges : optional function to get bridges to a list
*
2021-11-19 04:55:53 +03:00
* This simple version of the register function should be sufficient for most users .
* The fpga_region_register_full ( ) function is available for users that need to
* pass additional , optional parameters .
2018-10-16 01:20:03 +03:00
*
2021-11-19 04:55:53 +03:00
* Return : struct fpga_region or ERR_PTR ( )
2018-10-16 01:20:03 +03:00
*/
2021-11-19 04:55:53 +03:00
struct fpga_region *
fpga_region_register ( struct device * parent , struct fpga_manager * mgr ,
int ( * get_bridges ) ( struct fpga_region * ) )
2018-10-16 01:20:03 +03:00
{
2021-11-19 04:55:53 +03:00
struct fpga_region_info info = { 0 } ;
2018-10-16 01:20:03 +03:00
2021-11-19 04:55:53 +03:00
info . mgr = mgr ;
info . get_bridges = get_bridges ;
2018-10-16 01:20:03 +03:00
2021-11-19 04:55:53 +03:00
return fpga_region_register_full ( parent , & info ) ;
2017-11-15 23:20:23 +03:00
}
EXPORT_SYMBOL_GPL ( fpga_region_register ) ;
2018-05-17 02:50:01 +03:00
/**
2021-06-09 00:23:47 +03:00
* fpga_region_unregister - unregister an FPGA region
2018-05-17 02:49:57 +03:00
* @ region : FPGA region
2018-10-16 01:20:03 +03:00
*
2021-06-09 00:23:47 +03:00
* This function is intended for use in an FPGA region driver ' s remove function .
2018-05-17 02:49:57 +03:00
*/
void fpga_region_unregister ( struct fpga_region * region )
2017-11-15 23:20:23 +03:00
{
device_unregister ( & region - > dev ) ;
}
EXPORT_SYMBOL_GPL ( fpga_region_unregister ) ;
2016-11-01 22:14:29 +03:00
static void fpga_region_dev_release ( struct device * dev )
{
2021-11-19 04:55:53 +03:00
struct fpga_region * region = to_fpga_region ( dev ) ;
2022-05-27 11:59:15 +03:00
ida_free ( & fpga_region_ida , region - > dev . id ) ;
2021-11-19 04:55:53 +03:00
kfree ( region ) ;
2016-11-01 22:14:29 +03:00
}
2023-08-11 10:30:43 +03:00
static const struct class fpga_region_class = {
. name = " fpga_region " ,
. dev_groups = fpga_region_groups ,
. dev_release = fpga_region_dev_release ,
} ;
2016-11-01 22:14:29 +03:00
/**
2023-07-06 17:27:54 +03:00
* fpga_region_init - creates the fpga_region class .
*
* Return : 0 on success or ERR_PTR ( ) on error .
2016-11-01 22:14:29 +03:00
*/
static int __init fpga_region_init ( void )
{
2023-08-11 10:30:43 +03:00
return class_register ( & fpga_region_class ) ;
2016-11-01 22:14:29 +03:00
}
static void __exit fpga_region_exit ( void )
{
2023-08-11 10:30:43 +03:00
class_unregister ( & fpga_region_class ) ;
2016-11-01 22:14:29 +03:00
ida_destroy ( & fpga_region_ida ) ;
}
subsys_initcall ( fpga_region_init ) ;
module_exit ( fpga_region_exit ) ;
MODULE_DESCRIPTION ( " FPGA Region " ) ;
2017-11-15 23:20:21 +03:00
MODULE_AUTHOR ( " Alan Tull <atull@kernel.org> " ) ;
2016-11-01 22:14:29 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;