FPGA Manager changes for 6.10-rc1

FPGA MGR core:
 
 - Marco's change adds module owner parameter for all registration APIs.
 
 FPGA test:
 
 - Macro's change uses KUnit devices instead of platform devices.
 
 DFL:
 
 - Peter's change cleans up unused symbols.
 
 Xlinux:
 
 - Charles adds SelectMAP interface reprogramming support.
 - Andy's header inclusion cleanup.
 
 Altera:
 
 - Krzysztof & Christophe's cleanup for drivers
 -----BEGIN PGP SIGNATURE-----
 
 iIkEABYIADEWIQSgSJpClIeaArXyudb8twOBpKCM2gUCZjBbQRMceWlsdW4ueHVA
 aW50ZWwuY29tAAoJEPy3A4GkoIzaPikA/AhgKjW8PrU1V7QGLs4fI2XsXYujy64+
 RFjFqFppw9l7AP9FZRuj5wpHHXJbGCRw75JocqBxLWAVZ9cUxsaEZFE9AQ==
 =haws
 -----END PGP SIGNATURE-----

Merge tag 'fpga-for-6.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/fpga/linux-fpga into char-misc-next

Xu writes:

FPGA Manager changes for 6.10-rc1

FPGA MGR core:

- Marco's change adds module owner parameter for all registration APIs.

FPGA test:

- Macro's change uses KUnit devices instead of platform devices.

DFL:

- Peter's change cleans up unused symbols.

Xlinux:

- Charles adds SelectMAP interface reprogramming support.
- Andy's header inclusion cleanup.

Altera:

- Krzysztof & Christophe's cleanup for drivers

* tag 'fpga-for-6.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/fpga/linux-fpga:
  fpga: region: add owner module and take its refcount
  fpga: dfl: remove unused member pdata from struct dfl_{afu,fme}
  fpga: dfl: remove unused function is_dfl_feature_present()
  fpga: ice40-spi: Don't use "proxy" headers
  fpga: tests: use KUnit devices instead of platform devices
  fpga: altera-cvp: Remove an unused field in struct altera_cvp_conf
  fpga: altera: drop driver owner assignment
  fpga: xilinx-core: add new gpio names for prog and init
  fpga: xilinx-selectmap: add new driver
  dt-bindings: fpga: xlnx,fpga-selectmap: add DT schema
  fpga: xilinx-spi: extract a common driver core
  fpga: bridge: add owner module and take its refcount
  fpga: manager: add owner module and take its refcount
This commit is contained in:
Greg Kroah-Hartman 2024-04-30 09:23:46 +02:00
commit 1ddfcad01d
27 changed files with 676 additions and 375 deletions

View file

@ -0,0 +1,86 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/fpga/xlnx,fpga-selectmap.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Xilinx SelectMAP FPGA interface
maintainers:
- Charles Perry <charles.perry@savoirfairelinux.com>
description: |
Xilinx 7 Series FPGAs support a method of loading the bitstream over a
parallel port named the SelectMAP interface in the documentation. Only
the x8 mode is supported where data is loaded at one byte per rising edge of
the clock, with the MSB of each byte presented to the D0 pin.
Datasheets:
https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
allOf:
- $ref: /schemas/memory-controllers/mc-peripheral-props.yaml#
properties:
compatible:
enum:
- xlnx,fpga-xc7s-selectmap
- xlnx,fpga-xc7a-selectmap
- xlnx,fpga-xc7k-selectmap
- xlnx,fpga-xc7v-selectmap
reg:
description:
At least 1 byte of memory mapped IO
maxItems: 1
prog-gpios:
description:
config pin (referred to as PROGRAM_B in the manual)
maxItems: 1
done-gpios:
description:
config status pin (referred to as DONE in the manual)
maxItems: 1
init-gpios:
description:
initialization status and configuration error pin
(referred to as INIT_B in the manual)
maxItems: 1
csi-gpios:
description:
chip select pin (referred to as CSI_B in the manual)
Optional gpio for if the bus controller does not provide a chip select.
maxItems: 1
rdwr-gpios:
description:
read/write select pin (referred to as RDWR_B in the manual)
Optional gpio for if the bus controller does not provide this pin.
maxItems: 1
required:
- compatible
- reg
- prog-gpios
- done-gpios
- init-gpios
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
fpga-mgr@8000000 {
compatible = "xlnx,fpga-xc7s-selectmap";
reg = <0x8000000 0x4>;
prog-gpios = <&gpio5 5 GPIO_ACTIVE_LOW>;
init-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
done-gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>;
csi-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
rdwr-gpios = <&gpio3 10 GPIO_ACTIVE_LOW>;
};
...

View file

@ -6,9 +6,12 @@ API to implement a new FPGA bridge
* struct fpga_bridge - The FPGA Bridge structure
* struct fpga_bridge_ops - Low level Bridge driver ops
* fpga_bridge_register() - Create and register a bridge
* __fpga_bridge_register() - Create and register a bridge
* fpga_bridge_unregister() - Unregister a bridge
The helper macro ``fpga_bridge_register()`` automatically sets
the module that registers the FPGA bridge as the owner.
.. kernel-doc:: include/linux/fpga/fpga-bridge.h
:functions: fpga_bridge
@ -16,7 +19,7 @@ API to implement a new FPGA bridge
:functions: fpga_bridge_ops
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_register
:functions: __fpga_bridge_register
.. kernel-doc:: drivers/fpga/fpga-bridge.c
:functions: fpga_bridge_unregister

View file

@ -24,7 +24,8 @@ How to support a new FPGA device
--------------------------------
To add another FPGA manager, write a driver that implements a set of ops. The
probe function calls fpga_mgr_register() or fpga_mgr_register_full(), such as::
probe function calls ``fpga_mgr_register()`` or ``fpga_mgr_register_full()``,
such as::
static const struct fpga_manager_ops socfpga_fpga_ops = {
.write_init = socfpga_fpga_ops_configure_init,
@ -69,10 +70,11 @@ probe function calls fpga_mgr_register() or fpga_mgr_register_full(), such as::
}
Alternatively, the probe function could call one of the resource managed
register functions, devm_fpga_mgr_register() or devm_fpga_mgr_register_full().
When these functions are used, the parameter syntax is the same, but the call
to fpga_mgr_unregister() should be removed. In the above example, the
socfpga_fpga_remove() function would not be required.
register functions, ``devm_fpga_mgr_register()`` or
``devm_fpga_mgr_register_full()``. When these functions are used, the
parameter syntax is the same, but the call to ``fpga_mgr_unregister()`` should be
removed. In the above example, the ``socfpga_fpga_remove()`` function would not be
required.
The ops will implement whatever device specific register writes are needed to
do the programming sequence for this particular FPGA. These ops return 0 for
@ -125,15 +127,19 @@ API for implementing a new FPGA Manager driver
* struct fpga_manager - the FPGA manager struct
* struct fpga_manager_ops - Low level FPGA manager driver ops
* struct fpga_manager_info - Parameter structure for fpga_mgr_register_full()
* fpga_mgr_register_full() - Create and register an FPGA manager using the
* __fpga_mgr_register_full() - Create and register an FPGA manager using the
fpga_mgr_info structure to provide the full flexibility of options
* fpga_mgr_register() - Create and register an FPGA manager using standard
* __fpga_mgr_register() - Create and register an FPGA manager using standard
arguments
* devm_fpga_mgr_register_full() - Resource managed version of
fpga_mgr_register_full()
* devm_fpga_mgr_register() - Resource managed version of fpga_mgr_register()
* __devm_fpga_mgr_register_full() - Resource managed version of
__fpga_mgr_register_full()
* __devm_fpga_mgr_register() - Resource managed version of __fpga_mgr_register()
* fpga_mgr_unregister() - Unregister an FPGA manager
Helper macros ``fpga_mgr_register_full()``, ``fpga_mgr_register()``,
``devm_fpga_mgr_register_full()``, and ``devm_fpga_mgr_register()`` are available
to ease the registration.
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
:functions: fpga_mgr_states
@ -147,16 +153,16 @@ API for implementing a new FPGA Manager driver
:functions: fpga_manager_info
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_register_full
:functions: __fpga_mgr_register_full
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_register
:functions: __fpga_mgr_register
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: devm_fpga_mgr_register_full
:functions: __devm_fpga_mgr_register_full
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: devm_fpga_mgr_register
:functions: __devm_fpga_mgr_register
.. kernel-doc:: drivers/fpga/fpga-mgr.c
:functions: fpga_mgr_unregister

View file

@ -46,13 +46,16 @@ API to add a new FPGA region
----------------------------
* struct fpga_region - The FPGA region struct
* struct fpga_region_info - Parameter structure for fpga_region_register_full()
* fpga_region_register_full() - Create and register an FPGA region using the
* struct fpga_region_info - Parameter structure for __fpga_region_register_full()
* __fpga_region_register_full() - Create and register an FPGA region using the
fpga_region_info structure to provide the full flexibility of options
* fpga_region_register() - Create and register an FPGA region using standard
* __fpga_region_register() - Create and register an FPGA region using standard
arguments
* fpga_region_unregister() - Unregister an FPGA region
Helper macros ``fpga_region_register()`` and ``fpga_region_register_full()``
automatically set the module that registers the FPGA region as the owner.
The FPGA region's probe function will need to get a reference to the FPGA
Manager it will be using to do the programming. This usually would happen
during the region's probe function.
@ -82,10 +85,10 @@ following APIs to handle building or tearing down that list.
:functions: fpga_region_info
.. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_register_full
:functions: __fpga_region_register_full
.. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_register
:functions: __fpga_region_register
.. kernel-doc:: drivers/fpga/fpga-region.c
:functions: fpga_region_unregister

View file

@ -64,9 +64,21 @@ config FPGA_MGR_STRATIX10_SOC
help
FPGA manager driver support for the Intel Stratix10 SoC.
config FPGA_MGR_XILINX_CORE
tristate
config FPGA_MGR_XILINX_SELECTMAP
tristate "Xilinx Configuration over SelectMAP"
depends on HAS_IOMEM
select FPGA_MGR_XILINX_CORE
help
FPGA manager driver support for Xilinx FPGA configuration
over SelectMAP interface.
config FPGA_MGR_XILINX_SPI
tristate "Xilinx Configuration over Slave Serial (SPI)"
depends on SPI
select FPGA_MGR_XILINX_CORE
help
FPGA manager driver support for Xilinx FPGA configuration
over slave serial interface.

View file

@ -15,6 +15,8 @@ obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
obj-$(CONFIG_FPGA_MGR_STRATIX10_SOC) += stratix10-soc.o
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
obj-$(CONFIG_FPGA_MGR_XILINX_CORE) += xilinx-core.o
obj-$(CONFIG_FPGA_MGR_XILINX_SELECTMAP) += xilinx-selectmap.o
obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA) += zynqmp-fpga.o

View file

@ -72,7 +72,6 @@ static bool altera_cvp_chkcfg;
struct cvp_priv;
struct altera_cvp_conf {
struct fpga_manager *mgr;
struct pci_dev *pci_dev;
void __iomem *map;
void (*write_data)(struct altera_cvp_conf *conf,

View file

@ -284,7 +284,6 @@ MODULE_DEVICE_TABLE(spi, altera_ps_spi_ids);
static struct spi_driver altera_ps_driver = {
.driver = {
.name = "altera-ps-spi",
.owner = THIS_MODULE,
.of_match_table = of_ef_match,
},
.id_table = altera_ps_spi_ids,

View file

@ -858,8 +858,6 @@ static int afu_dev_init(struct platform_device *pdev)
if (!afu)
return -ENOMEM;
afu->pdata = pdata;
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, afu);
afu_mmio_region_init(pdata);

View file

@ -67,7 +67,6 @@ struct dfl_afu_dma_region {
* @regions: the mmio region linked list of this afu feature device.
* @dma_regions: root of dma regions rb tree.
* @num_umsgs: num of umsgs.
* @pdata: afu platform device's pdata.
*/
struct dfl_afu {
u64 region_cur_offset;
@ -75,8 +74,6 @@ struct dfl_afu {
u8 num_umsgs;
struct list_head regions;
struct rb_root dma_regions;
struct dfl_feature_platform_data *pdata;
};
/* hold pdata->lock when call __afu_port_enable/disable */

View file

@ -679,8 +679,6 @@ static int fme_dev_init(struct platform_device *pdev)
if (!fme)
return -ENOMEM;
fme->pdata = pdata;
mutex_lock(&pdata->lock);
dfl_fpga_pdata_set_private(pdata, fme);
mutex_unlock(&pdata->lock);

View file

@ -24,13 +24,11 @@
* @mgr: FME's FPGA manager platform device.
* @region_list: linked list of FME's FPGA regions.
* @bridge_list: linked list of FME's FPGA bridges.
* @pdata: fme platform device's pdata.
*/
struct dfl_fme {
struct platform_device *mgr;
struct list_head region_list;
struct list_head bridge_list;
struct dfl_feature_platform_data *pdata;
};
extern const struct dfl_feature_ops fme_pr_mgmt_ops;

View file

@ -437,11 +437,6 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id)
return NULL;
}
static inline bool is_dfl_feature_present(struct device *dev, u16 id)
{
return !!dfl_get_feature_ioaddr_by_id(dev, id);
}
static inline
struct device *dfl_fpga_pdata_to_parent(struct dfl_feature_platform_data *pdata)
{

View file

@ -55,33 +55,26 @@ int fpga_bridge_disable(struct fpga_bridge *bridge)
}
EXPORT_SYMBOL_GPL(fpga_bridge_disable);
static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
static struct fpga_bridge *__fpga_bridge_get(struct device *bridge_dev,
struct fpga_image_info *info)
{
struct fpga_bridge *bridge;
int ret = -ENODEV;
bridge = to_fpga_bridge(dev);
bridge = to_fpga_bridge(bridge_dev);
bridge->info = info;
if (!mutex_trylock(&bridge->mutex)) {
ret = -EBUSY;
goto err_dev;
}
if (!mutex_trylock(&bridge->mutex))
return ERR_PTR(-EBUSY);
if (!try_module_get(dev->parent->driver->owner))
goto err_ll_mod;
if (!try_module_get(bridge->br_ops_owner)) {
mutex_unlock(&bridge->mutex);
return ERR_PTR(-ENODEV);
}
dev_dbg(&bridge->dev, "get\n");
return bridge;
err_ll_mod:
mutex_unlock(&bridge->mutex);
err_dev:
put_device(dev);
return ERR_PTR(ret);
}
/**
@ -98,13 +91,18 @@ err_dev:
struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
struct fpga_image_info *info)
{
struct device *dev;
struct fpga_bridge *bridge;
struct device *bridge_dev;
dev = class_find_device_by_of_node(&fpga_bridge_class, np);
if (!dev)
bridge_dev = class_find_device_by_of_node(&fpga_bridge_class, np);
if (!bridge_dev)
return ERR_PTR(-ENODEV);
return __fpga_bridge_get(dev, info);
bridge = __fpga_bridge_get(bridge_dev, info);
if (IS_ERR(bridge))
put_device(bridge_dev);
return bridge;
}
EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
@ -125,6 +123,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data)
struct fpga_bridge *fpga_bridge_get(struct device *dev,
struct fpga_image_info *info)
{
struct fpga_bridge *bridge;
struct device *bridge_dev;
bridge_dev = class_find_device(&fpga_bridge_class, NULL, dev,
@ -132,7 +131,11 @@ struct fpga_bridge *fpga_bridge_get(struct device *dev,
if (!bridge_dev)
return ERR_PTR(-ENODEV);
return __fpga_bridge_get(bridge_dev, info);
bridge = __fpga_bridge_get(bridge_dev, info);
if (IS_ERR(bridge))
put_device(bridge_dev);
return bridge;
}
EXPORT_SYMBOL_GPL(fpga_bridge_get);
@ -146,7 +149,7 @@ void fpga_bridge_put(struct fpga_bridge *bridge)
dev_dbg(&bridge->dev, "put\n");
bridge->info = NULL;
module_put(bridge->dev.parent->driver->owner);
module_put(bridge->br_ops_owner);
mutex_unlock(&bridge->mutex);
put_device(&bridge->dev);
}
@ -316,18 +319,19 @@ static struct attribute *fpga_bridge_attrs[] = {
ATTRIBUTE_GROUPS(fpga_bridge);
/**
* fpga_bridge_register - create and register an FPGA Bridge device
* __fpga_bridge_register - create and register an FPGA Bridge device
* @parent: FPGA bridge device from pdev
* @name: FPGA bridge name
* @br_ops: pointer to structure of fpga bridge ops
* @priv: FPGA bridge private data
* @owner: owner module containing the br_ops
*
* Return: struct fpga_bridge pointer or ERR_PTR()
*/
struct fpga_bridge *
fpga_bridge_register(struct device *parent, const char *name,
const struct fpga_bridge_ops *br_ops,
void *priv)
__fpga_bridge_register(struct device *parent, const char *name,
const struct fpga_bridge_ops *br_ops,
void *priv, struct module *owner)
{
struct fpga_bridge *bridge;
int id, ret;
@ -357,6 +361,7 @@ fpga_bridge_register(struct device *parent, const char *name,
bridge->name = name;
bridge->br_ops = br_ops;
bridge->br_ops_owner = owner;
bridge->priv = priv;
bridge->dev.groups = br_ops->groups;
@ -386,7 +391,7 @@ error_kfree:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(fpga_bridge_register);
EXPORT_SYMBOL_GPL(__fpga_bridge_register);
/**
* fpga_bridge_unregister - unregister an FPGA bridge

View file

@ -664,20 +664,16 @@ static struct attribute *fpga_mgr_attrs[] = {
};
ATTRIBUTE_GROUPS(fpga_mgr);
static struct fpga_manager *__fpga_mgr_get(struct device *dev)
static struct fpga_manager *__fpga_mgr_get(struct device *mgr_dev)
{
struct fpga_manager *mgr;
mgr = to_fpga_manager(dev);
mgr = to_fpga_manager(mgr_dev);
if (!try_module_get(dev->parent->driver->owner))
goto err_dev;
if (!try_module_get(mgr->mops_owner))
mgr = ERR_PTR(-ENODEV);
return mgr;
err_dev:
put_device(dev);
return ERR_PTR(-ENODEV);
}
static int fpga_mgr_dev_match(struct device *dev, const void *data)
@ -693,12 +689,18 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
*/
struct fpga_manager *fpga_mgr_get(struct device *dev)
{
struct device *mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev,
fpga_mgr_dev_match);
struct fpga_manager *mgr;
struct device *mgr_dev;
mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev, fpga_mgr_dev_match);
if (!mgr_dev)
return ERR_PTR(-ENODEV);
return __fpga_mgr_get(mgr_dev);
mgr = __fpga_mgr_get(mgr_dev);
if (IS_ERR(mgr))
put_device(mgr_dev);
return mgr;
}
EXPORT_SYMBOL_GPL(fpga_mgr_get);
@ -711,13 +713,18 @@ EXPORT_SYMBOL_GPL(fpga_mgr_get);
*/
struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
{
struct device *dev;
struct fpga_manager *mgr;
struct device *mgr_dev;
dev = class_find_device_by_of_node(&fpga_mgr_class, node);
if (!dev)
mgr_dev = class_find_device_by_of_node(&fpga_mgr_class, node);
if (!mgr_dev)
return ERR_PTR(-ENODEV);
return __fpga_mgr_get(dev);
mgr = __fpga_mgr_get(mgr_dev);
if (IS_ERR(mgr))
put_device(mgr_dev);
return mgr;
}
EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
@ -727,7 +734,7 @@ EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
*/
void fpga_mgr_put(struct fpga_manager *mgr)
{
module_put(mgr->dev.parent->driver->owner);
module_put(mgr->mops_owner);
put_device(&mgr->dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_put);
@ -766,9 +773,10 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
/**
* fpga_mgr_register_full - create and register an FPGA Manager device
* __fpga_mgr_register_full - create and register an FPGA Manager device
* @parent: fpga manager device from pdev
* @info: parameters for fpga manager
* @owner: owner module containing the ops
*
* The caller of this function is responsible for calling fpga_mgr_unregister().
* Using devm_fpga_mgr_register_full() instead is recommended.
@ -776,7 +784,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
struct fpga_manager *
fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
struct module *owner)
{
const struct fpga_manager_ops *mops = info->mops;
struct fpga_manager *mgr;
@ -804,6 +813,8 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
mutex_init(&mgr->ref_mutex);
mgr->mops_owner = owner;
mgr->name = info->name;
mgr->mops = info->mops;
mgr->priv = info->priv;
@ -841,14 +852,15 @@ error_kfree:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
EXPORT_SYMBOL_GPL(__fpga_mgr_register_full);
/**
* fpga_mgr_register - create and register an FPGA Manager device
* __fpga_mgr_register - create and register an FPGA Manager device
* @parent: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
* @owner: owner module containing the ops
*
* The caller of this function is responsible for calling fpga_mgr_unregister().
* Using devm_fpga_mgr_register() instead is recommended. This simple
@ -859,8 +871,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
*/
struct fpga_manager *
fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv)
__fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv, struct module *owner)
{
struct fpga_manager_info info = { 0 };
@ -868,9 +880,9 @@ fpga_mgr_register(struct device *parent, const char *name,
info.mops = mops;
info.priv = priv;
return fpga_mgr_register_full(parent, &info);
return __fpga_mgr_register_full(parent, &info, owner);
}
EXPORT_SYMBOL_GPL(fpga_mgr_register);
EXPORT_SYMBOL_GPL(__fpga_mgr_register);
/**
* fpga_mgr_unregister - unregister an FPGA manager
@ -900,9 +912,10 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
}
/**
* devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
* __devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
* @parent: fpga manager device from pdev
* @info: parameters for fpga manager
* @owner: owner module containing the ops
*
* Return: fpga manager pointer on success, negative error code otherwise.
*
@ -910,7 +923,8 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
* function will be called automatically when the managing device is detached.
*/
struct fpga_manager *
devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
struct module *owner)
{
struct fpga_mgr_devres *dr;
struct fpga_manager *mgr;
@ -919,7 +933,7 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
if (!dr)
return ERR_PTR(-ENOMEM);
mgr = fpga_mgr_register_full(parent, info);
mgr = __fpga_mgr_register_full(parent, info, owner);
if (IS_ERR(mgr)) {
devres_free(dr);
return mgr;
@ -930,14 +944,15 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
return mgr;
}
EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register_full);
/**
* devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
* __devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
* @parent: fpga manager device from pdev
* @name: fpga manager name
* @mops: pointer to structure of fpga manager ops
* @priv: fpga manager private data
* @owner: owner module containing the ops
*
* Return: fpga manager pointer on success, negative error code otherwise.
*
@ -946,8 +961,9 @@ EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
* device is detached.
*/
struct fpga_manager *
devm_fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv)
__devm_fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv,
struct module *owner)
{
struct fpga_manager_info info = { 0 };
@ -955,9 +971,9 @@ devm_fpga_mgr_register(struct device *parent, const char *name,
info.mops = mops;
info.priv = priv;
return devm_fpga_mgr_register_full(parent, &info);
return __devm_fpga_mgr_register_full(parent, &info, owner);
}
EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register);
static void fpga_mgr_dev_release(struct device *dev)
{

View file

@ -53,7 +53,7 @@ static struct fpga_region *fpga_region_get(struct fpga_region *region)
}
get_device(dev);
if (!try_module_get(dev->parent->driver->owner)) {
if (!try_module_get(region->ops_owner)) {
put_device(dev);
mutex_unlock(&region->mutex);
return ERR_PTR(-ENODEV);
@ -75,7 +75,7 @@ static void fpga_region_put(struct fpga_region *region)
dev_dbg(dev, "put\n");
module_put(dev->parent->driver->owner);
module_put(region->ops_owner);
put_device(dev);
mutex_unlock(&region->mutex);
}
@ -181,14 +181,16 @@ static struct attribute *fpga_region_attrs[] = {
ATTRIBUTE_GROUPS(fpga_region);
/**
* fpga_region_register_full - create and register an FPGA Region device
* __fpga_region_register_full - create and register an FPGA Region device
* @parent: device parent
* @info: parameters for FPGA Region
* @owner: module containing the get_bridges function
*
* Return: struct fpga_region or ERR_PTR()
*/
struct fpga_region *
fpga_region_register_full(struct device *parent, const struct fpga_region_info *info)
__fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
struct module *owner)
{
struct fpga_region *region;
int id, ret = 0;
@ -213,6 +215,7 @@ fpga_region_register_full(struct device *parent, const struct fpga_region_info *
region->compat_id = info->compat_id;
region->priv = info->priv;
region->get_bridges = info->get_bridges;
region->ops_owner = owner;
mutex_init(&region->mutex);
INIT_LIST_HEAD(&region->bridge_list);
@ -241,13 +244,14 @@ err_free:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(fpga_region_register_full);
EXPORT_SYMBOL_GPL(__fpga_region_register_full);
/**
* fpga_region_register - create and register an FPGA Region device
* __fpga_region_register - create and register an FPGA Region device
* @parent: device parent
* @mgr: manager that programs this region
* @get_bridges: optional function to get bridges to a list
* @owner: module containing the get_bridges function
*
* 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
@ -256,17 +260,17 @@ EXPORT_SYMBOL_GPL(fpga_region_register_full);
* Return: struct fpga_region or ERR_PTR()
*/
struct fpga_region *
fpga_region_register(struct device *parent, struct fpga_manager *mgr,
int (*get_bridges)(struct fpga_region *))
__fpga_region_register(struct device *parent, struct fpga_manager *mgr,
int (*get_bridges)(struct fpga_region *), struct module *owner)
{
struct fpga_region_info info = { 0 };
info.mgr = mgr;
info.get_bridges = get_bridges;
return fpga_region_register_full(parent, &info);
return __fpga_region_register_full(parent, &info, owner);
}
EXPORT_SYMBOL_GPL(fpga_region_register);
EXPORT_SYMBOL_GPL(__fpga_region_register);
/**
* fpga_region_unregister - unregister an FPGA region

View file

@ -10,8 +10,8 @@
#include <linux/fpga/fpga-mgr.h>
#include <linux/gpio/consumer.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#include <linux/stringify.h>
@ -199,7 +199,7 @@ static struct spi_driver ice40_fpga_driver = {
.probe = ice40_fpga_probe,
.driver = {
.name = "ice40spi",
.of_match_table = of_match_ptr(ice40_fpga_of_match),
.of_match_table = ice40_fpga_of_match,
},
.id_table = ice40_fpga_spi_ids,
};

View file

@ -7,8 +7,8 @@
* Author: Marco Pagani <marpagan@redhat.com>
*/
#include <kunit/device.h>
#include <kunit/test.h>
#include <linux/device.h>
#include <linux/fpga/fpga-bridge.h>
#include <linux/module.h>
#include <linux/types.h>
@ -19,7 +19,7 @@ struct bridge_stats {
struct bridge_ctx {
struct fpga_bridge *bridge;
struct platform_device *pdev;
struct device *dev;
struct bridge_stats stats;
};
@ -43,30 +43,31 @@ static const struct fpga_bridge_ops fake_bridge_ops = {
/**
* register_test_bridge() - Register a fake FPGA bridge for testing.
* @test: KUnit test context object.
* @dev_name: name of the kunit device to be registered
*
* Return: Context of the newly registered FPGA bridge.
*/
static struct bridge_ctx *register_test_bridge(struct kunit *test)
static struct bridge_ctx *register_test_bridge(struct kunit *test, const char *dev_name)
{
struct bridge_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
ctx->pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
ctx->dev = kunit_device_register(test, dev_name);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
ctx->bridge = fpga_bridge_register(&ctx->pdev->dev, "Fake FPGA bridge", &fake_bridge_ops,
ctx->bridge = fpga_bridge_register(ctx->dev, "Fake FPGA bridge", &fake_bridge_ops,
&ctx->stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
return ctx;
}
static void unregister_test_bridge(struct bridge_ctx *ctx)
static void unregister_test_bridge(struct kunit *test, struct bridge_ctx *ctx)
{
fpga_bridge_unregister(ctx->bridge);
platform_device_unregister(ctx->pdev);
kunit_device_unregister(test, ctx->dev);
}
static void fpga_bridge_test_get(struct kunit *test)
@ -74,10 +75,10 @@ static void fpga_bridge_test_get(struct kunit *test)
struct bridge_ctx *ctx = test->priv;
struct fpga_bridge *bridge;
bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
bridge = fpga_bridge_get(ctx->dev, NULL);
KUNIT_EXPECT_PTR_EQ(test, bridge, ctx->bridge);
bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
bridge = fpga_bridge_get(ctx->dev, NULL);
KUNIT_EXPECT_EQ(test, PTR_ERR(bridge), -EBUSY);
fpga_bridge_put(ctx->bridge);
@ -105,19 +106,19 @@ static void fpga_bridge_test_get_put_list(struct kunit *test)
int ret;
ctx_0 = test->priv;
ctx_1 = register_test_bridge(test);
ctx_1 = register_test_bridge(test, "fpga-bridge-test-dev-1");
INIT_LIST_HEAD(&bridge_list);
/* Get bridge 0 and add it to the list */
ret = fpga_bridge_get_to_list(&ctx_0->pdev->dev, NULL, &bridge_list);
ret = fpga_bridge_get_to_list(ctx_0->dev, NULL, &bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, ctx_0->bridge,
list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
/* Get bridge 1 and add it to the list */
ret = fpga_bridge_get_to_list(&ctx_1->pdev->dev, NULL, &bridge_list);
ret = fpga_bridge_get_to_list(ctx_1->dev, NULL, &bridge_list);
KUNIT_EXPECT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, ctx_1->bridge,
@ -141,19 +142,19 @@ static void fpga_bridge_test_get_put_list(struct kunit *test)
KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list));
unregister_test_bridge(ctx_1);
unregister_test_bridge(test, ctx_1);
}
static int fpga_bridge_test_init(struct kunit *test)
{
test->priv = register_test_bridge(test);
test->priv = register_test_bridge(test, "fpga-bridge-test-dev-0");
return 0;
}
static void fpga_bridge_test_exit(struct kunit *test)
{
unregister_test_bridge(test->priv);
unregister_test_bridge(test, test->priv);
}
static struct kunit_case fpga_bridge_test_cases[] = {

View file

@ -7,8 +7,8 @@
* Author: Marco Pagani <marpagan@redhat.com>
*/
#include <kunit/device.h>
#include <kunit/test.h>
#include <linux/device.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
@ -40,7 +40,7 @@ struct mgr_stats {
struct mgr_ctx {
struct fpga_image_info *img_info;
struct fpga_manager *mgr;
struct platform_device *pdev;
struct device *dev;
struct mgr_stats stats;
};
@ -194,7 +194,7 @@ static void fpga_mgr_test_get(struct kunit *test)
struct mgr_ctx *ctx = test->priv;
struct fpga_manager *mgr;
mgr = fpga_mgr_get(&ctx->pdev->dev);
mgr = fpga_mgr_get(ctx->dev);
KUNIT_EXPECT_PTR_EQ(test, mgr, ctx->mgr);
fpga_mgr_put(ctx->mgr);
@ -284,14 +284,14 @@ static int fpga_mgr_test_init(struct kunit *test)
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
ctx->pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
ctx->dev = kunit_device_register(test, "fpga-manager-test-dev");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
ctx->mgr = devm_fpga_mgr_register(&ctx->pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
ctx->mgr = devm_fpga_mgr_register(ctx->dev, "Fake FPGA Manager", &fake_mgr_ops,
&ctx->stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
ctx->img_info = fpga_image_info_alloc(&ctx->pdev->dev);
ctx->img_info = fpga_image_info_alloc(ctx->dev);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->img_info);
test->priv = ctx;
@ -304,7 +304,7 @@ static void fpga_mgr_test_exit(struct kunit *test)
struct mgr_ctx *ctx = test->priv;
fpga_image_info_free(ctx->img_info);
platform_device_unregister(ctx->pdev);
kunit_device_unregister(test, ctx->dev);
}
static struct kunit_case fpga_mgr_test_cases[] = {

View file

@ -7,12 +7,12 @@
* Author: Marco Pagani <marpagan@redhat.com>
*/
#include <kunit/device.h>
#include <kunit/test.h>
#include <linux/fpga/fpga-bridge.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/fpga/fpga-region.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
struct mgr_stats {
@ -26,11 +26,11 @@ struct bridge_stats {
struct test_ctx {
struct fpga_manager *mgr;
struct platform_device *mgr_pdev;
struct device *mgr_dev;
struct fpga_bridge *bridge;
struct platform_device *bridge_pdev;
struct device *bridge_dev;
struct fpga_region *region;
struct platform_device *region_pdev;
struct device *region_dev;
struct bridge_stats bridge_stats;
struct mgr_stats mgr_stats;
};
@ -91,7 +91,7 @@ static void fpga_region_test_class_find(struct kunit *test)
struct test_ctx *ctx = test->priv;
struct fpga_region *region;
region = fpga_region_class_find(NULL, &ctx->region_pdev->dev, fake_region_match);
region = fpga_region_class_find(NULL, ctx->region_dev, fake_region_match);
KUNIT_EXPECT_PTR_EQ(test, region, ctx->region);
put_device(&region->dev);
@ -108,7 +108,7 @@ static void fpga_region_test_program_fpga(struct kunit *test)
char img_buf[4];
int ret;
img_info = fpga_image_info_alloc(&ctx->mgr_pdev->dev);
img_info = fpga_image_info_alloc(ctx->mgr_dev);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
img_info->buf = img_buf;
@ -148,32 +148,30 @@ static int fpga_region_test_init(struct kunit *test)
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
ctx->mgr_pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_pdev);
ctx->mgr_dev = kunit_device_register(test, "fpga-manager-test-dev");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_dev);
ctx->mgr = devm_fpga_mgr_register(&ctx->mgr_pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
&ctx->mgr_stats);
ctx->mgr = devm_fpga_mgr_register(ctx->mgr_dev, "Fake FPGA Manager",
&fake_mgr_ops, &ctx->mgr_stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
ctx->bridge_pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO,
NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_pdev);
ctx->bridge_dev = kunit_device_register(test, "fpga-bridge-test-dev");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_dev);
ctx->bridge = fpga_bridge_register(&ctx->bridge_pdev->dev, "Fake FPGA Bridge",
ctx->bridge = fpga_bridge_register(ctx->bridge_dev, "Fake FPGA Bridge",
&fake_bridge_ops, &ctx->bridge_stats);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
ctx->bridge_stats.enable = true;
ctx->region_pdev = platform_device_register_simple("region_pdev", PLATFORM_DEVID_AUTO,
NULL, 0);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_pdev);
ctx->region_dev = kunit_device_register(test, "fpga-region-test-dev");
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_dev);
region_info.mgr = ctx->mgr;
region_info.priv = ctx->bridge;
region_info.get_bridges = fake_region_get_bridges;
ctx->region = fpga_region_register_full(&ctx->region_pdev->dev, &region_info);
ctx->region = fpga_region_register_full(ctx->region_dev, &region_info);
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->region));
test->priv = ctx;
@ -186,18 +184,17 @@ static void fpga_region_test_exit(struct kunit *test)
struct test_ctx *ctx = test->priv;
fpga_region_unregister(ctx->region);
platform_device_unregister(ctx->region_pdev);
kunit_device_unregister(test, ctx->region_dev);
fpga_bridge_unregister(ctx->bridge);
platform_device_unregister(ctx->bridge_pdev);
kunit_device_unregister(test, ctx->bridge_dev);
platform_device_unregister(ctx->mgr_pdev);
kunit_device_unregister(test, ctx->mgr_dev);
}
static struct kunit_case fpga_region_test_cases[] = {
KUNIT_CASE(fpga_region_test_class_find),
KUNIT_CASE(fpga_region_test_program_fpga),
{}
};

229
drivers/fpga/xilinx-core.c Normal file
View file

@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Common parts of the Xilinx Spartan6 and 7 Series FPGA manager drivers.
*
* Copyright (C) 2017 DENX Software Engineering
*
* Anatolij Gustschin <agust@denx.de>
*/
#include "xilinx-core.h"
#include <linux/delay.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
static int get_done_gpio(struct fpga_manager *mgr)
{
struct xilinx_fpga_core *core = mgr->priv;
int ret;
ret = gpiod_get_value(core->done);
if (ret < 0)
dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret);
return ret;
}
static enum fpga_mgr_states xilinx_core_state(struct fpga_manager *mgr)
{
if (!get_done_gpio(mgr))
return FPGA_MGR_STATE_RESET;
return FPGA_MGR_STATE_UNKNOWN;
}
/**
* wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
* a given delay if the pin is unavailable
*
* @mgr: The FPGA manager object
* @value: Value INIT_B to wait for (1 = asserted = low)
* @alt_udelay: Delay to wait if the INIT_B GPIO is not available
*
* Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
* too much time passed waiting for that. If no INIT_B GPIO is available
* then always return 0.
*/
static int wait_for_init_b(struct fpga_manager *mgr, int value,
unsigned long alt_udelay)
{
struct xilinx_fpga_core *core = mgr->priv;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
if (core->init_b) {
while (time_before(jiffies, timeout)) {
int ret = gpiod_get_value(core->init_b);
if (ret == value)
return 0;
if (ret < 0) {
dev_err(&mgr->dev,
"Error reading INIT_B (%d)\n", ret);
return ret;
}
usleep_range(100, 400);
}
dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n",
value ? "assert" : "deassert");
return -ETIMEDOUT;
}
udelay(alt_udelay);
return 0;
}
static int xilinx_core_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info, const char *buf,
size_t count)
{
struct xilinx_fpga_core *core = mgr->priv;
int err;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
return -EINVAL;
}
gpiod_set_value(core->prog_b, 1);
err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
if (err) {
gpiod_set_value(core->prog_b, 0);
return err;
}
gpiod_set_value(core->prog_b, 0);
err = wait_for_init_b(mgr, 0, 0);
if (err)
return err;
if (get_done_gpio(mgr)) {
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
return -EIO;
}
/* program latency */
usleep_range(7500, 7600);
return 0;
}
static int xilinx_core_write(struct fpga_manager *mgr, const char *buf,
size_t count)
{
struct xilinx_fpga_core *core = mgr->priv;
return core->write(core, buf, count);
}
static int xilinx_core_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
struct xilinx_fpga_core *core = mgr->priv;
unsigned long timeout =
jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
bool expired = false;
int done;
int ret;
const char padding[1] = { 0xff };
/*
* This loop is carefully written such that if the driver is
* scheduled out for more than 'timeout', we still check for DONE
* before giving up and we apply 8 extra CCLK cycles in all cases.
*/
while (!expired) {
expired = time_after(jiffies, timeout);
done = get_done_gpio(mgr);
if (done < 0)
return done;
ret = core->write(core, padding, sizeof(padding));
if (ret)
return ret;
if (done)
return 0;
}
if (core->init_b) {
ret = gpiod_get_value(core->init_b);
if (ret < 0) {
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
return ret;
}
dev_err(&mgr->dev,
ret ? "CRC error or invalid device\n" :
"Missing sync word or incomplete bitstream\n");
} else {
dev_err(&mgr->dev, "Timeout after config data transfer\n");
}
return -ETIMEDOUT;
}
static inline struct gpio_desc *
xilinx_core_devm_gpiod_get(struct device *dev, const char *con_id,
const char *legacy_con_id, enum gpiod_flags flags)
{
struct gpio_desc *desc;
desc = devm_gpiod_get(dev, con_id, flags);
if (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT &&
of_device_is_compatible(dev->of_node, "xlnx,fpga-slave-serial"))
desc = devm_gpiod_get(dev, legacy_con_id, flags);
return desc;
}
static const struct fpga_manager_ops xilinx_core_ops = {
.state = xilinx_core_state,
.write_init = xilinx_core_write_init,
.write = xilinx_core_write,
.write_complete = xilinx_core_write_complete,
};
int xilinx_core_probe(struct xilinx_fpga_core *core)
{
struct fpga_manager *mgr;
if (!core || !core->dev || !core->write)
return -EINVAL;
/* PROGRAM_B is active low */
core->prog_b = xilinx_core_devm_gpiod_get(core->dev, "prog", "prog_b",
GPIOD_OUT_LOW);
if (IS_ERR(core->prog_b))
return dev_err_probe(core->dev, PTR_ERR(core->prog_b),
"Failed to get PROGRAM_B gpio\n");
core->init_b = xilinx_core_devm_gpiod_get(core->dev, "init", "init-b",
GPIOD_IN);
if (IS_ERR(core->init_b))
return dev_err_probe(core->dev, PTR_ERR(core->init_b),
"Failed to get INIT_B gpio\n");
core->done = devm_gpiod_get(core->dev, "done", GPIOD_IN);
if (IS_ERR(core->done))
return dev_err_probe(core->dev, PTR_ERR(core->done),
"Failed to get DONE gpio\n");
mgr = devm_fpga_mgr_register(core->dev,
"Xilinx Slave Serial FPGA Manager",
&xilinx_core_ops, core);
return PTR_ERR_OR_ZERO(mgr);
}
EXPORT_SYMBOL_GPL(xilinx_core_probe);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
MODULE_DESCRIPTION("Xilinx 7 Series FPGA manager core");

View file

@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __XILINX_CORE_H
#define __XILINX_CORE_H
#include <linux/device.h>
/**
* struct xilinx_fpga_core - interface between the driver and the core manager
* of Xilinx 7 Series FPGA manager
* @dev: device node
* @write: write callback of the driver
*/
struct xilinx_fpga_core {
/* public: */
struct device *dev;
int (*write)(struct xilinx_fpga_core *core, const char *buf,
size_t count);
/* private: handled by xilinx-core */
struct gpio_desc *prog_b;
struct gpio_desc *init_b;
struct gpio_desc *done;
};
int xilinx_core_probe(struct xilinx_fpga_core *core);
#endif /* __XILINX_CORE_H */

View file

@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Xilinx Spartan6 and 7 Series SelectMAP interface driver
*
* (C) 2024 Charles Perry <charles.perry@savoirfairelinux.com>
*
* Manage Xilinx FPGA firmware loaded over the SelectMAP configuration
* interface.
*/
#include "xilinx-core.h"
#include <linux/gpio/consumer.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/platform_device.h>
struct xilinx_selectmap_conf {
struct xilinx_fpga_core core;
void __iomem *base;
};
#define to_xilinx_selectmap_conf(obj) \
container_of(obj, struct xilinx_selectmap_conf, core)
static int xilinx_selectmap_write(struct xilinx_fpga_core *core,
const char *buf, size_t count)
{
struct xilinx_selectmap_conf *conf = to_xilinx_selectmap_conf(core);
size_t i;
for (i = 0; i < count; ++i)
writeb(buf[i], conf->base);
return 0;
}
static int xilinx_selectmap_probe(struct platform_device *pdev)
{
struct xilinx_selectmap_conf *conf;
struct gpio_desc *gpio;
void __iomem *base;
conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL);
if (!conf)
return -ENOMEM;
conf->core.dev = &pdev->dev;
conf->core.write = xilinx_selectmap_write;
base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(base))
return dev_err_probe(&pdev->dev, PTR_ERR(base),
"ioremap error\n");
conf->base = base;
/* CSI_B is active low */
gpio = devm_gpiod_get_optional(&pdev->dev, "csi", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
"Failed to get CSI_B gpio\n");
/* RDWR_B is active low */
gpio = devm_gpiod_get_optional(&pdev->dev, "rdwr", GPIOD_OUT_HIGH);
if (IS_ERR(gpio))
return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
"Failed to get RDWR_B gpio\n");
return xilinx_core_probe(&conf->core);
}
static const struct of_device_id xlnx_selectmap_of_match[] = {
{ .compatible = "xlnx,fpga-xc7s-selectmap", }, // Spartan-7
{ .compatible = "xlnx,fpga-xc7a-selectmap", }, // Artix-7
{ .compatible = "xlnx,fpga-xc7k-selectmap", }, // Kintex-7
{ .compatible = "xlnx,fpga-xc7v-selectmap", }, // Virtex-7
{},
};
MODULE_DEVICE_TABLE(of, xlnx_selectmap_of_match);
static struct platform_driver xilinx_selectmap_driver = {
.driver = {
.name = "xilinx-selectmap",
.of_match_table = xlnx_selectmap_of_match,
},
.probe = xilinx_selectmap_probe,
};
module_platform_driver(xilinx_selectmap_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Charles Perry <charles.perry@savoirfairelinux.com>");
MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SelectMap");

View file

@ -10,127 +10,17 @@
* the slave serial configuration interface.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/gpio/consumer.h>
#include "xilinx-core.h"
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/sizes.h>
struct xilinx_spi_conf {
struct spi_device *spi;
struct gpio_desc *prog_b;
struct gpio_desc *init_b;
struct gpio_desc *done;
};
static int get_done_gpio(struct fpga_manager *mgr)
{
struct xilinx_spi_conf *conf = mgr->priv;
int ret;
ret = gpiod_get_value(conf->done);
if (ret < 0)
dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret);
return ret;
}
static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
{
if (!get_done_gpio(mgr))
return FPGA_MGR_STATE_RESET;
return FPGA_MGR_STATE_UNKNOWN;
}
/**
* wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
* a given delay if the pin is unavailable
*
* @mgr: The FPGA manager object
* @value: Value INIT_B to wait for (1 = asserted = low)
* @alt_udelay: Delay to wait if the INIT_B GPIO is not available
*
* Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
* too much time passed waiting for that. If no INIT_B GPIO is available
* then always return 0.
*/
static int wait_for_init_b(struct fpga_manager *mgr, int value,
unsigned long alt_udelay)
{
struct xilinx_spi_conf *conf = mgr->priv;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
if (conf->init_b) {
while (time_before(jiffies, timeout)) {
int ret = gpiod_get_value(conf->init_b);
if (ret == value)
return 0;
if (ret < 0) {
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
return ret;
}
usleep_range(100, 400);
}
dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n",
value ? "assert" : "deassert");
return -ETIMEDOUT;
}
udelay(alt_udelay);
return 0;
}
static int xilinx_spi_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
struct xilinx_spi_conf *conf = mgr->priv;
int err;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
return -EINVAL;
}
gpiod_set_value(conf->prog_b, 1);
err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
if (err) {
gpiod_set_value(conf->prog_b, 0);
return err;
}
gpiod_set_value(conf->prog_b, 0);
err = wait_for_init_b(mgr, 0, 0);
if (err)
return err;
if (get_done_gpio(mgr)) {
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
return -EIO;
}
/* program latency */
usleep_range(7500, 7600);
return 0;
}
static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
static int xilinx_spi_write(struct xilinx_fpga_core *core, const char *buf,
size_t count)
{
struct xilinx_spi_conf *conf = mgr->priv;
struct spi_device *spi = to_spi_device(core->dev);
const char *fw_data = buf;
const char *fw_data_end = fw_data + count;
@ -141,9 +31,9 @@ static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
remaining = fw_data_end - fw_data;
stride = min_t(size_t, remaining, SZ_4K);
ret = spi_write(conf->spi, fw_data, stride);
ret = spi_write(spi, fw_data, stride);
if (ret) {
dev_err(&mgr->dev, "SPI error in firmware write: %d\n",
dev_err(core->dev, "SPI error in firmware write: %d\n",
ret);
return ret;
}
@ -153,109 +43,25 @@ static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
return 0;
}
static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf)
{
struct spi_device *spi = conf->spi;
const u8 din_data[1] = { 0xff };
int ret;
ret = spi_write(conf->spi, din_data, sizeof(din_data));
if (ret)
dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret);
return ret;
}
static int xilinx_spi_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
struct xilinx_spi_conf *conf = mgr->priv;
unsigned long timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
bool expired = false;
int done;
int ret;
/*
* This loop is carefully written such that if the driver is
* scheduled out for more than 'timeout', we still check for DONE
* before giving up and we apply 8 extra CCLK cycles in all cases.
*/
while (!expired) {
expired = time_after(jiffies, timeout);
done = get_done_gpio(mgr);
if (done < 0)
return done;
ret = xilinx_spi_apply_cclk_cycles(conf);
if (ret)
return ret;
if (done)
return 0;
}
if (conf->init_b) {
ret = gpiod_get_value(conf->init_b);
if (ret < 0) {
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
return ret;
}
dev_err(&mgr->dev,
ret ? "CRC error or invalid device\n"
: "Missing sync word or incomplete bitstream\n");
} else {
dev_err(&mgr->dev, "Timeout after config data transfer\n");
}
return -ETIMEDOUT;
}
static const struct fpga_manager_ops xilinx_spi_ops = {
.state = xilinx_spi_state,
.write_init = xilinx_spi_write_init,
.write = xilinx_spi_write,
.write_complete = xilinx_spi_write_complete,
};
static int xilinx_spi_probe(struct spi_device *spi)
{
struct xilinx_spi_conf *conf;
struct fpga_manager *mgr;
struct xilinx_fpga_core *core;
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
if (!conf)
core = devm_kzalloc(&spi->dev, sizeof(*core), GFP_KERNEL);
if (!core)
return -ENOMEM;
conf->spi = spi;
core->dev = &spi->dev;
core->write = xilinx_spi_write;
/* PROGRAM_B is active low */
conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW);
if (IS_ERR(conf->prog_b))
return dev_err_probe(&spi->dev, PTR_ERR(conf->prog_b),
"Failed to get PROGRAM_B gpio\n");
conf->init_b = devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN);
if (IS_ERR(conf->init_b))
return dev_err_probe(&spi->dev, PTR_ERR(conf->init_b),
"Failed to get INIT_B gpio\n");
conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
if (IS_ERR(conf->done))
return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
"Failed to get DONE gpio\n");
mgr = devm_fpga_mgr_register(&spi->dev,
"Xilinx Slave Serial FPGA Manager",
&xilinx_spi_ops, conf);
return PTR_ERR_OR_ZERO(mgr);
return xilinx_core_probe(core);
}
#ifdef CONFIG_OF
static const struct of_device_id xlnx_spi_of_match[] = {
{ .compatible = "xlnx,fpga-slave-serial", },
{
.compatible = "xlnx,fpga-slave-serial",
},
{}
};
MODULE_DEVICE_TABLE(of, xlnx_spi_of_match);

View file

@ -45,6 +45,7 @@ struct fpga_bridge_info {
* @dev: FPGA bridge device
* @mutex: enforces exclusive reference to bridge
* @br_ops: pointer to struct of FPGA bridge ops
* @br_ops_owner: module containing the br_ops
* @info: fpga image specific information
* @node: FPGA bridge list node
* @priv: low level driver private date
@ -54,6 +55,7 @@ struct fpga_bridge {
struct device dev;
struct mutex mutex; /* for exclusive reference to bridge */
const struct fpga_bridge_ops *br_ops;
struct module *br_ops_owner;
struct fpga_image_info *info;
struct list_head node;
void *priv;
@ -79,10 +81,12 @@ int of_fpga_bridge_get_to_list(struct device_node *np,
struct fpga_image_info *info,
struct list_head *bridge_list);
#define fpga_bridge_register(parent, name, br_ops, priv) \
__fpga_bridge_register(parent, name, br_ops, priv, THIS_MODULE)
struct fpga_bridge *
fpga_bridge_register(struct device *parent, const char *name,
const struct fpga_bridge_ops *br_ops,
void *priv);
__fpga_bridge_register(struct device *parent, const char *name,
const struct fpga_bridge_ops *br_ops, void *priv,
struct module *owner);
void fpga_bridge_unregister(struct fpga_bridge *br);
#endif /* _LINUX_FPGA_BRIDGE_H */

View file

@ -201,6 +201,7 @@ struct fpga_manager_ops {
* @state: state of fpga manager
* @compat_id: FPGA manager id for compatibility check.
* @mops: pointer to struct of fpga manager ops
* @mops_owner: module containing the mops
* @priv: low level driver private date
*/
struct fpga_manager {
@ -210,6 +211,7 @@ struct fpga_manager {
enum fpga_mgr_states state;
struct fpga_compat_id *compat_id;
const struct fpga_manager_ops *mops;
struct module *mops_owner;
void *priv;
};
@ -230,18 +232,30 @@ struct fpga_manager *fpga_mgr_get(struct device *dev);
void fpga_mgr_put(struct fpga_manager *mgr);
#define fpga_mgr_register_full(parent, info) \
__fpga_mgr_register_full(parent, info, THIS_MODULE)
struct fpga_manager *
fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info);
__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
struct module *owner);
#define fpga_mgr_register(parent, name, mops, priv) \
__fpga_mgr_register(parent, name, mops, priv, THIS_MODULE)
struct fpga_manager *
fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv);
__fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv, struct module *owner);
void fpga_mgr_unregister(struct fpga_manager *mgr);
#define devm_fpga_mgr_register_full(parent, info) \
__devm_fpga_mgr_register_full(parent, info, THIS_MODULE)
struct fpga_manager *
devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info);
__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
struct module *owner);
#define devm_fpga_mgr_register(parent, name, mops, priv) \
__devm_fpga_mgr_register(parent, name, mops, priv, THIS_MODULE)
struct fpga_manager *
devm_fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv);
__devm_fpga_mgr_register(struct device *parent, const char *name,
const struct fpga_manager_ops *mops, void *priv,
struct module *owner);
#endif /*_LINUX_FPGA_MGR_H */

View file

@ -36,6 +36,7 @@ struct fpga_region_info {
* @mgr: FPGA manager
* @info: FPGA image info
* @compat_id: FPGA region id for compatibility check.
* @ops_owner: module containing the get_bridges function
* @priv: private data
* @get_bridges: optional function to get bridges to a list
*/
@ -46,6 +47,7 @@ struct fpga_region {
struct fpga_manager *mgr;
struct fpga_image_info *info;
struct fpga_compat_id *compat_id;
struct module *ops_owner;
void *priv;
int (*get_bridges)(struct fpga_region *region);
};
@ -58,12 +60,17 @@ fpga_region_class_find(struct device *start, const void *data,
int fpga_region_program_fpga(struct fpga_region *region);
#define fpga_region_register_full(parent, info) \
__fpga_region_register_full(parent, info, THIS_MODULE)
struct fpga_region *
fpga_region_register_full(struct device *parent, const struct fpga_region_info *info);
__fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
struct module *owner);
#define fpga_region_register(parent, mgr, get_bridges) \
__fpga_region_register(parent, mgr, get_bridges, THIS_MODULE)
struct fpga_region *
fpga_region_register(struct device *parent, struct fpga_manager *mgr,
int (*get_bridges)(struct fpga_region *));
__fpga_region_register(struct device *parent, struct fpga_manager *mgr,
int (*get_bridges)(struct fpga_region *), struct module *owner);
void fpga_region_unregister(struct fpga_region *region);
#endif /* _FPGA_REGION_H */