2016-11-01 22:14:29 +03:00
/*
* FPGA Region - Device Tree support for FPGA programming under Linux
*
* 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
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# 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 ) ;
static struct class * fpga_region_class ;
2017-11-15 23:20:24 +03:00
struct fpga_region * fpga_region_class_find (
struct device * start , const void * data ,
int ( * match ) ( struct device * , const void * ) )
{
struct device * dev ;
dev = class_find_device ( fpga_region_class , start , data , match ) ;
if ( ! dev )
return NULL ;
return to_fpga_region ( dev ) ;
}
EXPORT_SYMBOL_GPL ( fpga_region_class_find ) ;
2016-11-01 22:14:29 +03:00
/**
* fpga_region_get - get an exclusive reference to a fpga region
* @ region : FPGA Region struct
*
* Caller should call fpga_region_put ( ) when done with region .
*
* Return fpga_region struct if successful .
* Return - EBUSY if someone already has a reference to the region .
* Return - ENODEV if @ np is not a FPGA Region .
*/
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
* @ region : FPGA region
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 .
2016-11-01 22:14:29 +03:00
* Return 0 for success or negative error code .
*/
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
2017-11-15 23:20:23 +03:00
int fpga_region_register ( struct device * dev , struct fpga_region * region )
2016-11-01 22:14:29 +03:00
{
int id , ret = 0 ;
id = ida_simple_get ( & fpga_region_ida , 0 , 0 , GFP_KERNEL ) ;
2017-11-15 23:20:23 +03:00
if ( id < 0 )
return id ;
2016-11-01 22:14:29 +03:00
mutex_init ( & region - > mutex ) ;
INIT_LIST_HEAD ( & region - > bridge_list ) ;
device_initialize ( & region - > dev ) ;
2017-11-15 23:20:28 +03:00
region - > dev . groups = region - > groups ;
2016-11-01 22:14:29 +03:00
region - > dev . class = fpga_region_class ;
region - > dev . parent = dev ;
2017-11-15 23:20:23 +03:00
region - > dev . of_node = dev - > 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 ;
ret = device_add ( & region - > dev ) ;
if ( ret )
goto err_remove ;
2017-11-15 23:20:23 +03:00
return 0 ;
err_remove :
ida_simple_remove ( & fpga_region_ida , id ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( fpga_region_register ) ;
int fpga_region_unregister ( struct fpga_region * region )
{
device_unregister ( & region - > dev ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( fpga_region_unregister ) ;
2016-11-01 22:14:29 +03:00
static void fpga_region_dev_release ( struct device * dev )
{
struct fpga_region * region = to_fpga_region ( dev ) ;
ida_simple_remove ( & fpga_region_ida , region - > dev . id ) ;
}
/**
* fpga_region_init - init function for fpga_region class
* Creates the fpga_region class and registers a reconfig notifier .
*/
static int __init fpga_region_init ( void )
{
fpga_region_class = class_create ( THIS_MODULE , " fpga_region " ) ;
if ( IS_ERR ( fpga_region_class ) )
return PTR_ERR ( fpga_region_class ) ;
fpga_region_class - > dev_release = fpga_region_dev_release ;
return 0 ;
}
static void __exit fpga_region_exit ( void )
{
class_destroy ( fpga_region_class ) ;
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 " ) ;