FPGA Manager changes for 7.2-rc1

- Wentao fixes use-after-free issue in of-fpga-region
 - Phil adds the of matching for Altera ts73xx fpga driver
 - Ian adds Efinix FPGA SPI programming driver
 - Andy fixes several minor issues for Lattice FPGA driver
 - Manish converts dt-bindings to YAML schema for several Altera devices
 - Sebastian fixes several minor issues for Intel DFL & Microchip FPGAs
 
 All patches have been reviewed on the mailing list, and have been in the
 last linux-next releases (as part of our for-next branch).
 
 Signed-off-by: Xu Yilun <yilun.xu@intel.com>
 -----BEGIN PGP SIGNATURE-----
 
 iIkEABYIADEWIQSgSJpClIeaArXyudb8twOBpKCM2gUCaiD6nRMceWlsdW4ueHVA
 aW50ZWwuY29tAAoJEPy3A4GkoIzaRK0BAL/tryz4yA9F/7hO5gmTT2ke+LfWn/pH
 7NmskFCd76svAQDSGvndrW6rquHdoD7A5ZTwY7MRY1NlFdoZCfUfxtt/Ag==
 =/REE
 -----END PGP SIGNATURE-----

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

Xu writes:

FPGA Manager changes for 7.2-rc1

- Wentao fixes use-after-free issue in of-fpga-region
- Phil adds the of matching for Altera ts73xx fpga driver
- Ian adds Efinix FPGA SPI programming driver
- Andy fixes several minor issues for Lattice FPGA driver
- Manish converts dt-bindings to YAML schema for several Altera devices
- Sebastian fixes several minor issues for Intel DFL & Microchip FPGAs

All patches have been reviewed on the mailing list, and have been in the
last linux-next releases (as part of our for-next branch).

Signed-off-by: Xu Yilun <yilun.xu@intel.com>

* tag 'fpga-for-7.2-rc1' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/fpga/linux-fpga:
  fpga: microchip-spi: fix zero header_size OOB read in mpf_ops_parse_header()
  fpga: dfl-afu: validate DMA mapping length in afu_dma_map_region()
  fpga: dfl: add bounds check in dfh_get_param_size()
  dt-bindings: fpga: altr,socfpga-fpga-mgr: convert to DT schema
  dt-bindings: fpga: altr,a10-pr-ip: convert to DT schema
  fpga: lattice-sysconfig-spi: simplify with spi_get_device_match_data()
  fpga: lattice-sysconfig-spi: Don't use "proxy" headers
  fpga: lattice-sysconfig-spi: Drop of_match_ptr() protection
  fpga: lattice-sysconfig-spi: Fix the terminator entries in ID tables
  fpga-mgr: Add Efinix SPI programming driver
  dt-bindings: fpga: Add Efinix SPI programming bindings
  dt-bindings: vendor-prefix: Add prefix for Efinix, Inc.
  fpga: ts73xx-fpga: add OF match table for device tree probing
  dt-bindings: fpga: Add Technologic Systems TS-7300 FPGA Manager
  fpga: region: fix use-after-free in child_regions_with_firmware()
This commit is contained in:
Greg Kroah-Hartman 2026-06-08 21:26:50 +02:00
commit b23338788b
16 changed files with 507 additions and 47 deletions

View File

@ -1,12 +0,0 @@
Altera Arria10 Partial Reconfiguration IP
Required properties:
- compatible : should contain "altr,a10-pr-ip"
- reg : base address and size for memory mapped io.
Example:
fpga_mgr: fpga-mgr@ff20c000 {
compatible = "altr,a10-pr-ip";
reg = <0xff20c000 0x10>;
};

View File

@ -1,17 +0,0 @@
Altera SOCFPGA FPGA Manager
Required properties:
- compatible : should contain "altr,socfpga-fpga-mgr"
- reg : base address and size for memory mapped io.
- The first index is for FPGA manager register access.
- The second index is for writing FPGA configuration data.
- interrupts : interrupt for the FPGA Manager device.
Example:
hps_0_fpgamgr: fpgamgr@ff706000 {
compatible = "altr,socfpga-fpga-mgr";
reg = <0xFF706000 0x1000
0xFFB90000 0x1000>;
interrupts = <0 175 4>;
};

View File

@ -0,0 +1,34 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/fpga/altr,a10-pr-ip.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Altera Arria10 Partial Reconfiguration IP
maintainers:
- Matthew Gerlach <matthew.gerlach@linux.intel.com>
description:
The Altera Arria 10 Partial Reconfiguration IP core allows the host
processor to perform partial reconfiguration of the FPGA fabric.
properties:
compatible:
const: altr,a10-pr-ip
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
fpga-mgr@ff20c000 {
compatible = "altr,a10-pr-ip";
reg = <0xff20c000 0x10>;
};

View File

@ -0,0 +1,38 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/fpga/altr,socfpga-fpga-mgr.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Altera SOCFPGA FPGA Manager
maintainers:
- Steffen Trumtrar <s.trumtrar@pengutronix.de>
properties:
compatible:
const: altr,socfpga-fpga-mgr
reg:
items:
- description: FPGA manager register access
- description: Writing FPGA configuration data
interrupts:
maxItems: 1
required:
- compatible
- reg
- interrupts
additionalProperties: false
examples:
- |
fpgamgr@ff706000 {
compatible = "altr,socfpga-fpga-mgr";
reg = <0xff706000 0x1000>,
<0xffb90000 0x1000>;
interrupts = <0 175 4>;
};

View File

@ -0,0 +1,98 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/fpga/efinix,trion-config.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Efinix SPI FPGA Manager
maintainers:
- Ian Dannapel <iansdannapel@gmail.com>
description: |
Efinix FPGAs (Trion, Topaz, and Titanium families) support loading bitstreams
through "SPI Passive Mode".
Additional pin hogs for bus width configuration should be set
elsewhere, if necessary.
References:
- https://www.efinixinc.com/docs/an006-configuring-trion-fpgas-v6.3.pdf
- https://www.efinixinc.com/docs/an033-configuring-titanium-fpgas-v2.8.pdf
- https://www.efinixinc.com/docs/an061-configuring-topaz-fpgas-v1.1.pdf
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
oneOf:
- items:
- enum:
- efinix,titanium-config
- efinix,topaz-config
- const: efinix,trion-config
- const: efinix,trion-config
spi-cpha: true
spi-cpol: true
spi-max-frequency:
maximum: 25000000
reg:
maxItems: 1
reset-gpios:
description:
reset and re-configuration trigger pin (low active)
maxItems: 1
cdone-gpios:
description:
optional configuration done status pin (high active)
maxItems: 1
required:
- compatible
- reg
- reset-gpios
- spi-cpha
- spi-cpol
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>;
fpga-mgr@0 {
compatible = "efinix,trion-config";
reg = <0>;
spi-max-frequency = <25000000>;
spi-cpha;
spi-cpol;
reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>;
cdone-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
};
};
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>;
fpga-mgr@0 {
compatible = "efinix,titanium-config", "efinix,trion-config";
reg = <0>;
spi-max-frequency = <25000000>;
spi-cpha;
spi-cpol;
reset-gpios = <&gpio4 17 GPIO_ACTIVE_LOW>;
cdone-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>;
};
};
...

View File

@ -0,0 +1,36 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/fpga/technologic,ts7300-fpga.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Technologic Systems TS-7300 FPGA Manager
maintainers:
- Florian Fainelli <f.fainelli@gmail.com>
description:
FPGA manager for the Altera Cyclone II FPGA on Technologic Systems
TS-7300 board. The FPGA is programmed via the memory-mapped interface
implemented in the CPLD.
properties:
compatible:
const: technologic,ts7300-fpga
reg:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
fpga-mgr@13c00000 {
compatible = "technologic,ts7300-fpga";
reg = <0x13c00000 0x2>;
};
...

View File

@ -489,6 +489,8 @@ patternProperties:
description: Emtop Embedded Solutions
"^eeti,.*":
description: eGalax_eMPIA Technology Inc
"^efinix,.*":
description: Efinix, Inc.
"^egnite,.*":
description: egnite GmbH
"^einfochips,.*":

View File

@ -288,6 +288,13 @@ config FPGA_MGR_LATTICE_SYSCONFIG_SPI
FPGA manager driver support for Lattice FPGAs programming over slave
SPI sysCONFIG interface.
config FPGA_MGR_EFINIX_SPI
tristate "Efinix FPGA configuration over SPI"
depends on SPI
help
FPGA manager driver support for Efinix FPGAs configuration over SPI
(passive mode only).
source "drivers/fpga/tests/Kconfig"
endif # FPGA

View File

@ -26,6 +26,7 @@ obj-$(CONFIG_FPGA_MGR_LATTICE_SYSCONFIG) += lattice-sysconfig.o
obj-$(CONFIG_FPGA_MGR_LATTICE_SYSCONFIG_SPI) += lattice-sysconfig-spi.o
obj-$(CONFIG_ALTERA_PR_IP_CORE) += altera-pr-ip-core.o
obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT) += altera-pr-ip-core-plat.o
obj-$(CONFIG_FPGA_MGR_EFINIX_SPI) += efinix-spi.o
# FPGA Secure Update Drivers
obj-$(CONFIG_FPGA_M10_BMC_SEC_UPDATE) += intel-m10-bmc-sec-update.o

View File

@ -723,6 +723,9 @@ afu_ioctl_dma_map(struct dfl_feature_dev_data *fdata, void __user *arg)
if (map.argsz < minsz || map.flags)
return -EINVAL;
if (map.length >> PAGE_SHIFT > (u64)INT_MAX)
return -EINVAL;
ret = afu_dma_map_region(fdata, map.user_addr, map.length, &map.iova);
if (ret)
return ret;

View File

@ -1132,6 +1132,8 @@ static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max)
return -EINVAL;
size += next * sizeof(u64);
if (size > max)
return -EINVAL;
if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v))
return size;

260
drivers/fpga/efinix-spi.c Normal file
View File

@ -0,0 +1,260 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* FPGA Manager Driver for Efinix
*
* Copyright (C) 2025 iris-GmbH infrared & intelligent sensors
*
* Ian Dannapel <iansdannapel@gmail.com>
*
* Load Efinix FPGA firmware over SPI using the serial configuration interface.
*
* Note: Only passive mode (host initiates transfer) is currently supported.
*/
#include <linux/delay.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
/*
* 13 dummy bytes generate 104 SPI clock cycles (8 bits each).
* Used to meet the requirement for >100 clock cycles idle sequence.
*/
#define EFINIX_SPI_IDLE_CYCLES_BYTES 13
/*
* tDMIN: Minimum time between deassertion of CRESET_N to first
* valid configuration data. (32 µs)
*/
#define EFINIX_TDMIN_US_MIN 35
#define EFINIX_TDMIN_US_MAX 40
/*
* tCRESET_N: Minimum CRESET_N low pulse width required to
* trigger re-configuration. (320 ns)
*/
#define EFINIX_TCRESETN_DELAY_MIN_US 1
#define EFINIX_TCRESETN_DELAY_MAX_US 2
/*
* tUSER: Minimum configuration duration after CDONE goes high
* before entering user mode. (25 µs)
*/
#define EFINIX_TUSER_US_MIN 30
#define EFINIX_TUSER_US_MAX 35
struct efinix_spi_conf {
struct spi_device *spi;
struct gpio_desc *cdone;
struct gpio_desc *reset;
};
static void efinix_spi_reset(struct efinix_spi_conf *conf)
{
gpiod_set_value(conf->reset, 1);
usleep_range(EFINIX_TCRESETN_DELAY_MIN_US, EFINIX_TCRESETN_DELAY_MAX_US);
gpiod_set_value(conf->reset, 0);
usleep_range(EFINIX_TDMIN_US_MIN, EFINIX_TDMIN_US_MAX);
}
static enum fpga_mgr_states efinix_spi_state(struct fpga_manager *mgr)
{
struct efinix_spi_conf *conf = mgr->priv;
if (conf->cdone && gpiod_get_value(conf->cdone) == 1)
return FPGA_MGR_STATE_OPERATING;
return FPGA_MGR_STATE_UNKNOWN;
}
static int efinix_spi_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
struct efinix_spi_conf *conf = mgr->priv;
struct spi_transfer assert_cs = {
/* Keep CS asserted across configuration. */
.cs_change = 1,
};
struct spi_message message;
int ret;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
return -EOPNOTSUPP;
}
/*
* Efinix passive SPI configuration requires chip select to stay
* asserted from reset until the bitstream is fully clocked in.
* Lock the SPI bus so no other device can toggle CS between the
* reset pulse and the write/complete transfers.
*/
spi_bus_lock(conf->spi->controller);
spi_message_init_with_transfers(&message, &assert_cs, 1);
ret = spi_sync_locked(conf->spi, &message);
if (ret) {
spi_bus_unlock(conf->spi->controller);
return ret;
}
/* Reset with CS asserted */
efinix_spi_reset(conf);
return 0;
}
static int efinix_spi_write(struct fpga_manager *mgr, const char *buf,
size_t count)
{
struct spi_transfer write_xfer = {
.tx_buf = buf,
.len = count,
.cs_change = 1,
};
struct efinix_spi_conf *conf = mgr->priv;
struct spi_message message;
int ret;
spi_message_init_with_transfers(&message, &write_xfer, 1);
ret = spi_sync_locked(conf->spi, &message);
if (ret) {
dev_err(&mgr->dev, "SPI error in firmware write: %d\n", ret);
spi_bus_unlock(conf->spi->controller);
}
return ret;
}
static int efinix_spi_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
unsigned long timeout =
jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
struct spi_transfer clk_cycles = {
.len = EFINIX_SPI_IDLE_CYCLES_BYTES,
/* Release CS after the trailing idle clocks are sent. */
.cs_change = 0,
};
struct efinix_spi_conf *conf = mgr->priv;
struct spi_message message;
int done, ret;
bool expired = false;
u8 *dummy_buf;
dummy_buf = kzalloc(EFINIX_SPI_IDLE_CYCLES_BYTES, GFP_KERNEL);
if (!dummy_buf) {
ret = -ENOMEM;
goto unlock_spi;
}
/*
* Keep the bus locked while sending the trailing idle clocks, then
* let this final transfer deassert CS to terminate configuration.
*/
clk_cycles.tx_buf = dummy_buf;
spi_message_init_with_transfers(&message, &clk_cycles, 1);
ret = spi_sync_locked(conf->spi, &message);
if (ret) {
dev_err(&mgr->dev, "SPI error in write complete: %d\n", ret);
goto free_buf;
}
if (conf->cdone) {
while (!expired) {
done = gpiod_get_value(conf->cdone);
if (done < 0) {
ret = done;
goto free_buf;
}
if (done)
break;
usleep_range(10, 20);
expired = time_after(jiffies, timeout);
}
if (expired) {
dev_err(&mgr->dev, "Timeout waiting for CDONE\n");
ret = -ETIMEDOUT;
goto free_buf;
}
}
usleep_range(EFINIX_TUSER_US_MIN, EFINIX_TUSER_US_MAX);
free_buf:
kfree(dummy_buf);
unlock_spi:
spi_bus_unlock(conf->spi->controller);
return ret;
}
static const struct fpga_manager_ops efinix_spi_ops = {
.state = efinix_spi_state,
.write_init = efinix_spi_write_init,
.write = efinix_spi_write,
.write_complete = efinix_spi_write_complete,
};
static int efinix_spi_probe(struct spi_device *spi)
{
struct efinix_spi_conf *conf;
struct fpga_manager *mgr;
if (!(spi->mode & SPI_CPHA) || !(spi->mode & SPI_CPOL))
return dev_err_probe(&spi->dev, -EINVAL,
"Unsupported SPI mode, set CPHA and CPOL\n");
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
if (!conf)
return -ENOMEM;
conf->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(conf->reset))
return dev_err_probe(&spi->dev, PTR_ERR(conf->reset),
"Failed to get RESET gpio\n");
conf->cdone = devm_gpiod_get_optional(&spi->dev, "cdone", GPIOD_IN);
if (IS_ERR(conf->cdone))
return dev_err_probe(&spi->dev, PTR_ERR(conf->cdone),
"Failed to get CDONE gpio\n");
conf->spi = spi;
mgr = devm_fpga_mgr_register(&spi->dev,
"Efinix FPGA Manager",
&efinix_spi_ops, conf);
return PTR_ERR_OR_ZERO(mgr);
}
static const struct of_device_id efinix_spi_of_match[] = {
{ .compatible = "efinix,trion-config", },
{}
};
MODULE_DEVICE_TABLE(of, efinix_spi_of_match);
static const struct spi_device_id efinix_ids[] = {
{ "trion-config", 0 },
{},
};
MODULE_DEVICE_TABLE(spi, efinix_ids);
static struct spi_driver efinix_spi_driver = {
.driver = {
.name = "efinix-spi",
.of_match_table = efinix_spi_of_match,
},
.probe = efinix_spi_probe,
.id_table = efinix_ids,
};
module_spi_driver(efinix_spi_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ian Dannapel <iansdannapel@gmail.com>");
MODULE_DESCRIPTION("Efinix FPGA SPI Programming Driver");

View File

@ -3,8 +3,14 @@
* Lattice FPGA programming over slave SPI sysCONFIG interface.
*/
#include <linux/of.h>
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
#include <linux/errno.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include "lattice-sysconfig.h"
@ -85,7 +91,6 @@ static int sysconfig_spi_bitstream_burst_complete(struct sysconfig_priv *priv)
static int sysconfig_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *dev_id;
struct device *dev = &spi->dev;
struct sysconfig_priv *priv;
const u32 *spi_max_speed;
@ -94,15 +99,7 @@ static int sysconfig_spi_probe(struct spi_device *spi)
if (!priv)
return -ENOMEM;
spi_max_speed = device_get_match_data(dev);
if (!spi_max_speed) {
dev_id = spi_get_device_id(spi);
if (!dev_id)
return -ENODEV;
spi_max_speed = (const u32 *)dev_id->driver_data;
}
spi_max_speed = spi_get_device_match_data(spi);
if (!spi_max_speed)
return -EINVAL;
@ -125,26 +122,26 @@ static const struct spi_device_id sysconfig_spi_ids[] = {
{
.name = "sysconfig-ecp5",
.driver_data = (kernel_ulong_t)&ecp5_spi_max_speed_hz,
}, {},
},
{}
};
MODULE_DEVICE_TABLE(spi, sysconfig_spi_ids);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id sysconfig_of_ids[] = {
{
.compatible = "lattice,sysconfig-ecp5",
.data = &ecp5_spi_max_speed_hz,
}, {},
},
{}
};
MODULE_DEVICE_TABLE(of, sysconfig_of_ids);
#endif /* IS_ENABLED(CONFIG_OF) */
static struct spi_driver lattice_sysconfig_driver = {
.probe = sysconfig_spi_probe,
.id_table = sysconfig_spi_ids,
.driver = {
.name = "lattice_sysconfig_spi_fpga_mgr",
.of_match_table = of_match_ptr(sysconfig_of_ids),
.of_match_table = sysconfig_of_ids,
},
};
module_spi_driver(lattice_sysconfig_driver);

View File

@ -116,6 +116,9 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr,
}
header_size = *(buf + MPF_HEADER_SIZE_OFFSET);
if (!header_size)
return -EINVAL;
if (header_size > count) {
info->header_size = header_size;
return -EAGAIN;

View File

@ -168,11 +168,10 @@ static int child_regions_with_firmware(struct device_node *overlay)
fpga_region_of_match);
}
of_node_put(child_region);
if (ret)
pr_err("firmware-name not allowed in child FPGA region: %pOF",
child_region);
of_node_put(child_region);
return ret;
}

View File

@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/string.h>
#include <linux/iopoll.h>
@ -119,9 +120,17 @@ static int ts73xx_fpga_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(mgr);
}
static const struct of_device_id ts73xx_fpga_of_match[] = {
{ .compatible = "technologic,ts7300-fpga" },
{},
};
MODULE_DEVICE_TABLE(of, ts73xx_fpga_of_match);
static struct platform_driver ts73xx_fpga_driver = {
.driver = {
.name = "ts73xx-fpga-mgr",
.of_match_table = ts73xx_fpga_of_match,
},
.probe = ts73xx_fpga_probe,
};