mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
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:
commit
1ddfcad01d
27 changed files with 676 additions and 375 deletions
|
@ -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>;
|
||||
};
|
||||
...
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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(®ion->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(®ion->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(®ion->mutex);
|
||||
INIT_LIST_HEAD(®ion->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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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(®ion->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, ®ion_info);
|
||||
ctx->region = fpga_region_register_full(ctx->region_dev, ®ion_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
229
drivers/fpga/xilinx-core.c
Normal 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");
|
27
drivers/fpga/xilinx-core.h
Normal file
27
drivers/fpga/xilinx-core.h
Normal 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 */
|
95
drivers/fpga/xilinx-selectmap.c
Normal file
95
drivers/fpga/xilinx-selectmap.c
Normal 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");
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Add table
Reference in a new issue