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 ) ;
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
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
*
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
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
/**
* fpga_region_create - alloc and init a struct fpga_region
* @ dev : device parent
* @ mgr : manager that programs this region
* @ get_bridges : optional function to get bridges to a list
*
2018-10-16 01:20:03 +03:00
* The caller of this function is responsible for freeing the resulting region
* struct with fpga_region_free ( ) . Using devm_fpga_region_create ( ) instead is
* recommended .
*
2018-05-17 02:49:57 +03:00
* Return : struct fpga_region or NULL
*/
struct fpga_region
* fpga_region_create ( struct device * dev ,
struct fpga_manager * mgr ,
int ( * get_bridges ) ( struct fpga_region * ) )
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 ;
2018-05-17 02:49:57 +03:00
region = kzalloc ( sizeof ( * region ) , GFP_KERNEL ) ;
if ( ! region )
return NULL ;
2016-11-01 22:14:29 +03:00
id = ida_simple_get ( & fpga_region_ida , 0 , 0 , GFP_KERNEL ) ;
2017-11-15 23:20:23 +03:00
if ( id < 0 )
2018-05-17 02:49:57 +03:00
goto err_free ;
2016-11-01 22:14:29 +03:00
2018-05-17 02:49:57 +03:00
region - > mgr = mgr ;
region - > get_bridges = 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
2016-11-01 22:14:29 +03:00
device_initialize ( & region - > dev ) ;
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 ;
2018-05-17 02:49:57 +03:00
return region ;
2017-11-15 23:20:23 +03:00
err_remove :
ida_simple_remove ( & fpga_region_ida , id ) ;
2018-05-17 02:49:57 +03:00
err_free :
kfree ( region ) ;
return NULL ;
}
EXPORT_SYMBOL_GPL ( fpga_region_create ) ;
/**
2018-10-16 01:20:03 +03:00
* fpga_region_free - free a FPGA region created by fpga_region_create ( )
* @ region : FPGA region
2018-05-17 02:49:57 +03:00
*/
void fpga_region_free ( struct fpga_region * region )
{
ida_simple_remove ( & fpga_region_ida , region - > dev . id ) ;
kfree ( region ) ;
}
EXPORT_SYMBOL_GPL ( fpga_region_free ) ;
2018-10-16 01:20:03 +03:00
static void devm_fpga_region_release ( struct device * dev , void * res )
{
struct fpga_region * region = * ( struct fpga_region * * ) res ;
fpga_region_free ( region ) ;
}
/**
* devm_fpga_region_create - create and initialize a managed FPGA region struct
* @ dev : device parent
* @ mgr : manager that programs this region
* @ get_bridges : optional function to get bridges to a list
*
* This function is intended for use in a FPGA region driver ' s probe function .
* After the region driver creates the region struct with
* devm_fpga_region_create ( ) , it should register it with fpga_region_register ( ) .
* The region driver ' s remove function should call fpga_region_unregister ( ) .
* The region struct allocated with this function will be freed automatically on
* driver detach . This includes the case of a probe function returning error
* before calling fpga_region_register ( ) , the struct will still get cleaned up .
*
* Return : struct fpga_region or NULL
*/
struct fpga_region
* devm_fpga_region_create ( struct device * dev ,
struct fpga_manager * mgr ,
int ( * get_bridges ) ( struct fpga_region * ) )
{
struct fpga_region * * ptr , * region ;
ptr = devres_alloc ( devm_fpga_region_release , sizeof ( * ptr ) , GFP_KERNEL ) ;
if ( ! ptr )
return NULL ;
region = fpga_region_create ( dev , mgr , get_bridges ) ;
if ( ! region ) {
devres_free ( ptr ) ;
} else {
* ptr = region ;
devres_add ( dev , ptr ) ;
}
return region ;
}
EXPORT_SYMBOL_GPL ( devm_fpga_region_create ) ;
2018-05-17 02:50:01 +03:00
/**
2018-05-17 02:49:57 +03:00
* fpga_region_register - register a FPGA region
2018-10-16 01:20:03 +03:00
* @ region : FPGA region
*
2018-05-17 02:49:57 +03:00
* Return : 0 or - errno
*/
int fpga_region_register ( struct fpga_region * region )
{
return device_add ( & region - > dev ) ;
2017-11-15 23:20:23 +03:00
}
EXPORT_SYMBOL_GPL ( fpga_region_register ) ;
2018-05-17 02:50:01 +03:00
/**
2018-10-16 01:20:03 +03:00
* fpga_region_unregister - unregister a FPGA region
2018-05-17 02:49:57 +03:00
* @ region : FPGA region
2018-10-16 01:20:03 +03:00
*
* This function is intended for use in a 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 )
{
}
/**
* 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 ) ;
2018-06-30 03:53:12 +03:00
fpga_region_class - > dev_groups = fpga_region_groups ;
2016-11-01 22:14:29 +03:00
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 " ) ;