2019-05-29 17:17:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-03-24 18:33:21 +03:00
/*
* Copyright ( c ) 2017 , National Instruments Corp .
2021-02-11 08:11:48 +03:00
* Copyright ( c ) 2017 , Xilinx Inc
2017-03-24 18:33:21 +03:00
*
* FPGA Bridge Driver for the Xilinx LogiCORE Partial Reconfiguration
* Decoupler IP Core .
*/
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/of_device.h>
# include <linux/module.h>
# include <linux/fpga/fpga-bridge.h>
# define CTRL_CMD_DECOUPLE BIT(0)
# define CTRL_CMD_COUPLE 0
# define CTRL_OFFSET 0
2021-02-11 08:11:48 +03:00
struct xlnx_config_data {
const char * name ;
} ;
2017-03-24 18:33:21 +03:00
struct xlnx_pr_decoupler_data {
2021-02-11 08:11:48 +03:00
const struct xlnx_config_data * ipconfig ;
2017-03-24 18:33:21 +03:00
void __iomem * io_base ;
struct clk * clk ;
} ;
static inline void xlnx_pr_decoupler_write ( struct xlnx_pr_decoupler_data * d ,
u32 offset , u32 val )
{
writel ( val , d - > io_base + offset ) ;
}
static inline u32 xlnx_pr_decouple_read ( const struct xlnx_pr_decoupler_data * d ,
u32 offset )
{
return readl ( d - > io_base + offset ) ;
}
static int xlnx_pr_decoupler_enable_set ( struct fpga_bridge * bridge , bool enable )
{
int err ;
struct xlnx_pr_decoupler_data * priv = bridge - > priv ;
err = clk_enable ( priv - > clk ) ;
if ( err )
return err ;
if ( enable )
xlnx_pr_decoupler_write ( priv , CTRL_OFFSET , CTRL_CMD_COUPLE ) ;
else
xlnx_pr_decoupler_write ( priv , CTRL_OFFSET , CTRL_CMD_DECOUPLE ) ;
clk_disable ( priv - > clk ) ;
return 0 ;
}
static int xlnx_pr_decoupler_enable_show ( struct fpga_bridge * bridge )
{
const struct xlnx_pr_decoupler_data * priv = bridge - > priv ;
u32 status ;
int err ;
err = clk_enable ( priv - > clk ) ;
if ( err )
return err ;
status = readl ( priv - > io_base ) ;
clk_disable ( priv - > clk ) ;
return ! status ;
}
2017-09-21 17:52:41 +03:00
static const struct fpga_bridge_ops xlnx_pr_decoupler_br_ops = {
2017-03-24 18:33:21 +03:00
. enable_set = xlnx_pr_decoupler_enable_set ,
. enable_show = xlnx_pr_decoupler_enable_show ,
} ;
2021-07-02 06:54:03 +03:00
# ifdef CONFIG_OF
2021-02-11 08:11:48 +03:00
static const struct xlnx_config_data decoupler_config = {
. name = " Xilinx PR Decoupler " ,
} ;
static const struct xlnx_config_data shutdown_config = {
. name = " Xilinx DFX AXI Shutdown Manager " ,
} ;
2017-03-24 18:33:21 +03:00
static const struct of_device_id xlnx_pr_decoupler_of_match [ ] = {
2021-02-11 08:11:48 +03:00
{ . compatible = " xlnx,pr-decoupler-1.00 " , . data = & decoupler_config } ,
{ . compatible = " xlnx,pr-decoupler " , . data = & decoupler_config } ,
{ . compatible = " xlnx,dfx-axi-shutdown-manager-1.00 " ,
. data = & shutdown_config } ,
{ . compatible = " xlnx,dfx-axi-shutdown-manager " ,
. data = & shutdown_config } ,
2017-03-24 18:33:21 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , xlnx_pr_decoupler_of_match ) ;
2021-07-02 06:54:03 +03:00
# endif
2017-03-24 18:33:21 +03:00
static int xlnx_pr_decoupler_probe ( struct platform_device * pdev )
{
2021-02-11 08:11:48 +03:00
struct device_node * np = pdev - > dev . of_node ;
2017-03-24 18:33:21 +03:00
struct xlnx_pr_decoupler_data * priv ;
2018-05-17 02:49:56 +03:00
struct fpga_bridge * br ;
2017-03-24 18:33:21 +03:00
int err ;
struct resource * res ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2021-02-11 08:11:48 +03:00
if ( np ) {
const struct of_device_id * match ;
match = of_match_node ( xlnx_pr_decoupler_of_match , np ) ;
if ( match & & match - > data )
priv - > ipconfig = match - > data ;
}
2017-03-24 18:33:21 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
priv - > io_base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( priv - > io_base ) )
return PTR_ERR ( priv - > io_base ) ;
priv - > clk = devm_clk_get ( & pdev - > dev , " aclk " ) ;
2021-02-04 16:36:11 +03:00
if ( IS_ERR ( priv - > clk ) )
return dev_err_probe ( & pdev - > dev , PTR_ERR ( priv - > clk ) ,
" input clock not found \n " ) ;
2017-03-24 18:33:21 +03:00
err = clk_prepare_enable ( priv - > clk ) ;
if ( err ) {
dev_err ( & pdev - > dev , " unable to enable clock \n " ) ;
return err ;
}
clk_disable ( priv - > clk ) ;
2021-11-19 04:55:52 +03:00
br = fpga_bridge_register ( & pdev - > dev , priv - > ipconfig - > name ,
& xlnx_pr_decoupler_br_ops , priv ) ;
if ( IS_ERR ( br ) ) {
err = PTR_ERR ( br ) ;
2021-02-11 08:11:48 +03:00
dev_err ( & pdev - > dev , " unable to register %s " ,
priv - > ipconfig - > name ) ;
2018-05-17 02:49:56 +03:00
goto err_clk ;
2017-03-24 18:33:21 +03:00
}
2021-11-19 04:55:52 +03:00
platform_set_drvdata ( pdev , br ) ;
2017-03-24 18:33:21 +03:00
return 0 ;
2018-05-17 02:49:56 +03:00
err_clk :
clk_unprepare ( priv - > clk ) ;
return err ;
2017-03-24 18:33:21 +03:00
}
static int xlnx_pr_decoupler_remove ( struct platform_device * pdev )
{
struct fpga_bridge * bridge = platform_get_drvdata ( pdev ) ;
struct xlnx_pr_decoupler_data * p = bridge - > priv ;
2018-05-17 02:49:56 +03:00
fpga_bridge_unregister ( bridge ) ;
2017-03-24 18:33:21 +03:00
clk_unprepare ( p - > clk ) ;
return 0 ;
}
static struct platform_driver xlnx_pr_decoupler_driver = {
. probe = xlnx_pr_decoupler_probe ,
. remove = xlnx_pr_decoupler_remove ,
. driver = {
. name = " xlnx_pr_decoupler " ,
. of_match_table = of_match_ptr ( xlnx_pr_decoupler_of_match ) ,
} ,
} ;
module_platform_driver ( xlnx_pr_decoupler_driver ) ;
MODULE_DESCRIPTION ( " Xilinx Partial Reconfiguration Decoupler " ) ;
MODULE_AUTHOR ( " Moritz Fischer <mdf@kernel.org> " ) ;
MODULE_AUTHOR ( " Michal Simek <michal.simek@xilinx.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;