All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/2] Lattice ECP5 FPGA manager
@ 2022-08-15 13:21 Ivan Bornyakov
  2022-08-15 13:21 ` [PATCH v6 1/2] fpga: ecp5-spi: add " Ivan Bornyakov
  2022-08-15 13:21 ` [PATCH v6 2/2] dt-bindings: fpga: add binding doc for ecp5-spi fpga mgr Ivan Bornyakov
  0 siblings, 2 replies; 9+ messages in thread
From: Ivan Bornyakov @ 2022-08-15 13:21 UTC (permalink / raw)
  To: mdf, hao.wu, yilun.xu, trix, dg, robh+dt, krzysztof.kozlowski+dt
  Cc: Ivan Bornyakov, linux-fpga, devicetree, linux-kernel, system

Add support to the FPGA manager for programming Lattice ECP5 FPGA over
slave SPI interface with .bit formatted uncompressed bitstream image.

ChangeLog:
  v1 -> v2:
    * remove "spi" from compatible string
    * reword description in dt-bindings doc
    * add reference to spi-peripheral-props.yaml in dt-binding doc
    * fix DTS example in dt-bindings doc: 4-spaces indentations, no
      undersores in node names.
  v2 -> v3:
    * fix typo "##size-cells" -> "#size-cells" in dt-bindings example
  v3 -> v4:
    * dt-bindings: reword description
    * dt-bindings: revert props order
  v4 -> v5:
    * dt-bindings: remove trailing dot from title
    * dt-bindings: reword description to avoid driver reference
    * dt-bindings: add "Reviewed-by: Krzysztof Kozlowski" tag
  v5 -> v6:
    * ecp5-spi: lock SPI bus for exclusive usage in
      ecp5_ops_write_init(), release in ecp5_ops_write_complete()
      or on error

Ivan Bornyakov (2):
  fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  dt-bindings: fpga: add binding doc for ecp5-spi fpga mgr

 .../bindings/fpga/lattice,ecp5-fpga-mgr.yaml  |  74 +++++
 drivers/fpga/Kconfig                          |   7 +
 drivers/fpga/Makefile                         |   1 +
 drivers/fpga/ecp5-spi.c                       | 311 ++++++++++++++++++
 4 files changed, 393 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fpga/lattice,ecp5-fpga-mgr.yaml
 create mode 100644 drivers/fpga/ecp5-spi.c

-- 
2.37.1



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v6 1/2] fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  2022-08-15 13:21 [PATCH v6 0/2] Lattice ECP5 FPGA manager Ivan Bornyakov
@ 2022-08-15 13:21 ` Ivan Bornyakov
  2022-08-16  2:00   ` Xu Yilun
  2022-08-15 13:21 ` [PATCH v6 2/2] dt-bindings: fpga: add binding doc for ecp5-spi fpga mgr Ivan Bornyakov
  1 sibling, 1 reply; 9+ messages in thread
From: Ivan Bornyakov @ 2022-08-15 13:21 UTC (permalink / raw)
  To: mdf, hao.wu, yilun.xu, trix, dg, robh+dt, krzysztof.kozlowski+dt
  Cc: Ivan Bornyakov, linux-fpga, devicetree, linux-kernel, system

Add support to the FPGA manager for programming Lattice ECP5 FPGA over
slave SPI interface with .bit formatted uncompressed bitstream image.

Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
---
 drivers/fpga/Kconfig    |   7 +
 drivers/fpga/Makefile   |   1 +
 drivers/fpga/ecp5-spi.c | 311 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 319 insertions(+)
 create mode 100644 drivers/fpga/ecp5-spi.c

diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index 6c416955da53..920277a08ed9 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -263,4 +263,11 @@ config FPGA_MGR_MICROCHIP_SPI
 	  programming over slave SPI interface with .dat formatted
 	  bitstream image.
 
+config FPGA_MGR_ECP5_SPI
+	tristate "Lattice ECP5 SPI FPGA manager"
+	depends on SPI
+	help
+	  FPGA manager driver support for Lattice ECP5 programming over slave
+	  SPI interface with .bit formatted uncompressed bitstream image.
+
 endif # FPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 42ae8b58abce..17c7a3c4b385 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA)	+= zynq-fpga.o
 obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA)	+= zynqmp-fpga.o
 obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA)	+= versal-fpga.o
 obj-$(CONFIG_FPGA_MGR_MICROCHIP_SPI)	+= microchip-spi.o
+obj-$(CONFIG_FPGA_MGR_ECP5_SPI)		+= ecp5-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
 
diff --git a/drivers/fpga/ecp5-spi.c b/drivers/fpga/ecp5-spi.c
new file mode 100644
index 000000000000..aa0dd10823a3
--- /dev/null
+++ b/drivers/fpga/ecp5-spi.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lattice ECP5 FPGA programming over slave SPI interface.
+ */
+
+#include <linux/delay.h>
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/spi/spi.h>
+
+#define	ECP5_SPI_MAX_SPEED_HZ		60000000
+
+#define	ECP5_SPI_ISC_ENABLE		{0xC6, 0x00, 0x00, 0x00}
+#define	ECP5_SPI_ISC_DISABLE		{0x26, 0x00, 0x00, 0x00}
+#define	ECP5_SPI_ISC_ERASE		{0x0E, 0x01, 0x00, 0x00}
+#define	ECP5_SPI_LSC_INIT_ADDR		{0x46, 0x00, 0x00, 0x00}
+#define	ECP5_SPI_LSC_BITSTREAM_BURST	{0x7a, 0x00, 0x00, 0x00}
+#define	ECP5_SPI_LSC_CHECK_BUSY		{0xF0, 0x00, 0x00, 0x00}
+
+#define ECP5_POLL_RETRIES		100000
+
+struct ecp5_priv {
+	struct gpio_desc *program;
+	struct gpio_desc *init;
+	struct gpio_desc *done;
+	struct spi_device *spi;
+};
+
+static enum fpga_mgr_states ecp5_ops_state(struct fpga_manager *mgr)
+{
+	struct ecp5_priv *priv = mgr->priv;
+
+	return gpiod_get_value(priv->done) ? FPGA_MGR_STATE_OPERATING :
+					     FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int ecp5_poll_busy(struct spi_device *spi)
+{
+	const u8 lsc_check_busy[] = ECP5_SPI_LSC_CHECK_BUSY;
+	int ret, retries = ECP5_POLL_RETRIES;
+	struct spi_transfer xfers[2] = { 0 };
+	struct spi_message msg;
+	u8 busy;
+
+	xfers[0].tx_buf = lsc_check_busy;
+	xfers[0].len = sizeof(lsc_check_busy);
+	xfers[1].rx_buf = &busy;
+	xfers[1].len = sizeof(busy);
+
+	while (retries--) {
+		spi_message_init_with_transfers(&msg, xfers, ARRAY_SIZE(xfers));
+		ret = spi_sync_locked(spi, &msg);
+		if (ret)
+			return ret;
+
+		if (!busy)
+			return 0;
+
+		usleep_range(50, 100);
+	}
+
+	return -EBUSY;
+}
+
+static int ecp5_poll_gpio(struct gpio_desc *gpio, bool is_active)
+{
+	int value, retries = ECP5_POLL_RETRIES;
+
+	while (retries--) {
+		value = gpiod_get_value(gpio);
+		if (value < 0)
+			return value;
+
+		if ((!is_active && !value) || (is_active && value))
+			return 0;
+
+		ndelay(10);
+	}
+
+	return -EFAULT;
+}
+
+static int ecp5_ops_write_init(struct fpga_manager *mgr,
+			       struct fpga_image_info *info,
+			       const char *buf, size_t count)
+{
+	const u8 lsc_bitstream_burst[] = ECP5_SPI_LSC_BITSTREAM_BURST;
+	const u8 lsc_init_addr[] = ECP5_SPI_LSC_INIT_ADDR;
+	const u8 isc_enable[] = ECP5_SPI_ISC_ENABLE;
+	const u8 isc_erase[] = ECP5_SPI_ISC_ERASE;
+	struct ecp5_priv *priv = mgr->priv;
+	struct spi_device *spi = priv->spi;
+	struct device *dev = &mgr->dev;
+	struct spi_transfer isc_xfers[] = {
+		{
+			.tx_buf = isc_enable,
+			.len = sizeof(isc_enable),
+			.cs_change = 1,
+		}, {
+			.tx_buf = isc_erase,
+			.len = sizeof(isc_erase),
+		},
+	};
+	struct spi_transfer lsc_xfers[] = {
+		{
+			.tx_buf = lsc_init_addr,
+			.len = sizeof(lsc_init_addr),
+			.cs_change = 1,
+		}, {
+			.tx_buf = lsc_bitstream_burst,
+			.len = sizeof(lsc_bitstream_burst),
+			.cs_change = 1,
+		},
+	};
+	struct spi_message msg;
+	int ret;
+
+	if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
+		dev_err(dev, "Partial reconfiguration is not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* Enter init mode */
+	gpiod_set_value(priv->program, 1);
+
+	ret = ecp5_poll_gpio(priv->init, true);
+	if (!ret)
+		ret = ecp5_poll_gpio(priv->done, false);
+
+	if (ret) {
+		dev_err(dev, "Failed to go to initialization mode\n");
+		return ret;
+	}
+
+	/* Enter program mode */
+	gpiod_set_value(priv->program, 0);
+
+	ret = ecp5_poll_gpio(priv->init, false);
+	if (ret) {
+		dev_err(dev, "Failed to go to program mode\n");
+		return ret;
+	}
+
+	/*
+	 * Lock SPI bus for exclusive usage until FPGA programming is done.
+	 * SPI bus will be released in ecp5_ops_write_complete() or on error.
+	 */
+	spi_bus_lock(spi->controller);
+
+	/* Enter ISC mode */
+	spi_message_init_with_transfers(&msg, isc_xfers, ARRAY_SIZE(isc_xfers));
+	ret = spi_sync_locked(spi, &msg);
+	if (!ret)
+		ret = ecp5_poll_busy(spi);
+
+	if (ret) {
+		dev_err(dev, "Failed to go to ISC mode\n");
+		goto out;
+	}
+
+	/* Prepare for bitstream burst write */
+	spi_message_init_with_transfers(&msg, lsc_xfers, ARRAY_SIZE(lsc_xfers));
+	ret = spi_sync_locked(spi, &msg);
+	if (ret)
+		dev_err(dev, "Failed to prepare for bitstream burst write\n");
+
+out:
+	if (ret)
+		spi_bus_unlock(spi->controller);
+
+	return ret;
+}
+
+static int ecp5_ops_write(struct fpga_manager *mgr, const char *buf, size_t count)
+{
+	struct ecp5_priv *priv = mgr->priv;
+	struct spi_device *spi = priv->spi;
+	struct spi_transfer xfer = {
+		.tx_buf = buf,
+		.len = count,
+		.cs_change = 1,
+	};
+	struct spi_message msg;
+	int ret;
+
+	spi_message_init_with_transfers(&msg, &xfer, 1);
+	ret = spi_sync_locked(spi, &msg);
+	if (ret)
+		spi_bus_unlock(spi->controller);
+
+	return ret;
+}
+
+static int ecp5_ops_write_complete(struct fpga_manager *mgr,
+				   struct fpga_image_info *info)
+{
+	const u8 isc_disable[] = ECP5_SPI_ISC_DISABLE;
+	struct ecp5_priv *priv = mgr->priv;
+	struct spi_device *spi = priv->spi;
+	struct spi_transfer xfer = { 0 };
+	struct device *dev = &mgr->dev;
+	struct spi_message msg;
+	int ret;
+
+	/* Toggle CS and wait for bitstream write to finish */
+	spi_message_init_with_transfers(&msg, &xfer, 1);
+	ret = spi_sync_locked(spi, &msg);
+	if (!ret)
+		ret = ecp5_poll_busy(spi);
+
+	if (ret) {
+		dev_err(dev, "Error while waiting bitstream write to finish\n");
+		goto out;
+	}
+
+	/* Exit ISC mode */
+	xfer.tx_buf = isc_disable;
+	xfer.len = sizeof(isc_disable);
+	spi_message_init_with_transfers(&msg, &xfer, 1);
+	ret = spi_sync_locked(spi, &msg);
+	if (!ret)
+		ret = ecp5_poll_gpio(priv->done, true);
+
+	if (ret)
+		dev_err(dev, "Failed to finish ISC\n");
+
+out:
+	spi_bus_unlock(spi->controller);
+
+	return ret;
+}
+
+static const struct fpga_manager_ops ecp5_fpga_ops = {
+	.state = ecp5_ops_state,
+	.write_init = ecp5_ops_write_init,
+	.write = ecp5_ops_write,
+	.write_complete = ecp5_ops_write_complete,
+};
+
+static int ecp5_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct fpga_manager *mgr;
+	struct ecp5_priv *priv;
+	int ret;
+
+	if (spi->max_speed_hz > ECP5_SPI_MAX_SPEED_HZ) {
+		dev_err(dev, "SPI speed %u is too high, maximum speed is %u\n",
+			spi->max_speed_hz, ECP5_SPI_MAX_SPEED_HZ);
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->spi = spi;
+
+	priv->done = devm_gpiod_get(dev, "done", GPIOD_IN);
+	if (IS_ERR(priv->done)) {
+		ret = PTR_ERR(priv->done);
+		dev_err(dev, "Failed to get DONE GPIO: %d\n", ret);
+		return ret;
+	}
+
+	priv->init = devm_gpiod_get(dev, "init", GPIOD_IN);
+	if (IS_ERR(priv->init)) {
+		ret = PTR_ERR(priv->init);
+		dev_err(dev, "Failed to get INIT GPIO: %d\n", ret);
+		return ret;
+	}
+
+	priv->program = devm_gpiod_get(dev, "program", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->program)) {
+		ret = PTR_ERR(priv->program);
+		dev_err(dev, "Failed to get PROGRAM GPIO: %d\n", ret);
+		return ret;
+	}
+
+	mgr = devm_fpga_mgr_register(dev, "Lattice ECP5 SPI FPGA Manager",
+				     &ecp5_fpga_ops, priv);
+
+	return PTR_ERR_OR_ZERO(mgr);
+}
+
+static const struct spi_device_id ecp5_spi_ids[] = {
+	{ .name = "ecp5-fpga-mgr" },
+	{},
+};
+MODULE_DEVICE_TABLE(spi, ecp5_spi_ids);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ecp5_of_ids[] = {
+	{ .compatible = "lattice,ecp5-fpga-mgr" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ecp5_of_ids);
+#endif /* IS_ENABLED(CONFIG_OF) */
+
+static struct spi_driver ecp5_driver = {
+	.probe = ecp5_probe,
+	.id_table = ecp5_spi_ids,
+	.driver = {
+		.name = "lattice_ecp5_spi_fpga_mgr",
+		.of_match_table = of_match_ptr(ecp5_of_ids),
+	},
+};
+
+module_spi_driver(ecp5_driver);
+
+MODULE_DESCRIPTION("Lattice ECP5 SPI FPGA Manager");
+MODULE_LICENSE("GPL");
-- 
2.37.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v6 2/2] dt-bindings: fpga: add binding doc for ecp5-spi fpga mgr
  2022-08-15 13:21 [PATCH v6 0/2] Lattice ECP5 FPGA manager Ivan Bornyakov
  2022-08-15 13:21 ` [PATCH v6 1/2] fpga: ecp5-spi: add " Ivan Bornyakov
@ 2022-08-15 13:21 ` Ivan Bornyakov
  1 sibling, 0 replies; 9+ messages in thread
From: Ivan Bornyakov @ 2022-08-15 13:21 UTC (permalink / raw)
  To: mdf, hao.wu, yilun.xu, trix, dg, robh+dt, krzysztof.kozlowski+dt
  Cc: Ivan Bornyakov, linux-fpga, devicetree, linux-kernel, system,
	Krzysztof Kozlowski

Add Device Tree Binding doc for Lattice ECP5 FPGA manager using slave
SPI to load .bit formatted uncompressed bitstream image.

Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
 .../bindings/fpga/lattice,ecp5-fpga-mgr.yaml  | 74 +++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fpga/lattice,ecp5-fpga-mgr.yaml

diff --git a/Documentation/devicetree/bindings/fpga/lattice,ecp5-fpga-mgr.yaml b/Documentation/devicetree/bindings/fpga/lattice,ecp5-fpga-mgr.yaml
new file mode 100644
index 000000000000..34693a3c2f1e
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/lattice,ecp5-fpga-mgr.yaml
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/fpga/lattice,ecp5-fpga-mgr.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Lattice ECP5 Slave SPI FPGA manager
+
+maintainers:
+  - Ivan Bornyakov <i.bornyakov@metrotek.ru>
+
+description:
+  Lattice ECP5 sysCONFIG port, which is used for device configuration, among
+  others, have Slave Serial Peripheral Interface. Only full reconfiguration
+  with uncompressed bitstream image in .bit format is supported.
+
+allOf:
+  - $ref: /schemas/spi/spi-peripheral-props.yaml
+
+properties:
+  compatible:
+    enum:
+      - lattice,ecp5-fpga-mgr
+
+  reg:
+    maxItems: 1
+
+  spi-max-frequency:
+    maximum: 60000000
+
+  program-gpios:
+    description:
+      A GPIO line connected to PROGRAMN (active low) pin of the device.
+      Initiates configuration sequence.
+    maxItems: 1
+
+  init-gpios:
+    description:
+      A GPIO line connected to INITN (active low) pin of the device.
+      Indicates that the FPGA is ready to be configured.
+    maxItems: 1
+
+  done-gpios:
+    description:
+      A GPIO line connected to DONE (active high) pin of the device.
+      Indicates that the configuration sequence is complete.
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - program-gpios
+  - init-gpios
+  - done-gpios
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    spi {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        fpga-mgr@0 {
+            compatible = "lattice,ecp5-fpga-mgr";
+            reg = <0>;
+            spi-max-frequency = <20000000>;
+            program-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>;
+            init-gpios = <&gpio3 3 GPIO_ACTIVE_LOW>;
+            done-gpios = <&gpio3 2 GPIO_ACTIVE_HIGH>;
+        };
+    };
-- 
2.37.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 1/2] fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  2022-08-15 13:21 ` [PATCH v6 1/2] fpga: ecp5-spi: add " Ivan Bornyakov
@ 2022-08-16  2:00   ` Xu Yilun
  2022-08-16  4:58     ` Ivan Bornyakov
  0 siblings, 1 reply; 9+ messages in thread
From: Xu Yilun @ 2022-08-16  2:00 UTC (permalink / raw)
  To: Ivan Bornyakov
  Cc: mdf, hao.wu, trix, dg, robh+dt, krzysztof.kozlowski+dt,
	linux-fpga, devicetree, linux-kernel, system

On 2022-08-15 at 16:21:56 +0300, Ivan Bornyakov wrote:
> Add support to the FPGA manager for programming Lattice ECP5 FPGA over
> slave SPI interface with .bit formatted uncompressed bitstream image.

Not sure if something is missed.

https://lore.kernel.org/all/20220729145757.GA2601292@yilunxu-OptiPlex-7050/

I was considering if a generic driver for lattice slave SPI sysCONFIG
interface could be introduced. From machxo2 & ecp5 Programming Usage
Guide, or others in this series, they basically use the same reconfigure
interface & protocol.

Thanks,
Yilun

> 
> Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
> ---
>  drivers/fpga/Kconfig    |   7 +
>  drivers/fpga/Makefile   |   1 +
>  drivers/fpga/ecp5-spi.c | 311 ++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 319 insertions(+)
>  create mode 100644 drivers/fpga/ecp5-spi.c
> 
> diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> index 6c416955da53..920277a08ed9 100644
> --- a/drivers/fpga/Kconfig
> +++ b/drivers/fpga/Kconfig
> @@ -263,4 +263,11 @@ config FPGA_MGR_MICROCHIP_SPI
>  	  programming over slave SPI interface with .dat formatted
>  	  bitstream image.
>  
> +config FPGA_MGR_ECP5_SPI
> +	tristate "Lattice ECP5 SPI FPGA manager"
> +	depends on SPI
> +	help
> +	  FPGA manager driver support for Lattice ECP5 programming over slave
> +	  SPI interface with .bit formatted uncompressed bitstream image.
> +
>  endif # FPGA
> diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> index 42ae8b58abce..17c7a3c4b385 100644
> --- a/drivers/fpga/Makefile
> +++ b/drivers/fpga/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA)	+= zynq-fpga.o
>  obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA)	+= zynqmp-fpga.o
>  obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA)	+= versal-fpga.o
>  obj-$(CONFIG_FPGA_MGR_MICROCHIP_SPI)	+= microchip-spi.o
> +obj-$(CONFIG_FPGA_MGR_ECP5_SPI)		+= ecp5-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
>  
> diff --git a/drivers/fpga/ecp5-spi.c b/drivers/fpga/ecp5-spi.c
> new file mode 100644
> index 000000000000..aa0dd10823a3
> --- /dev/null
> +++ b/drivers/fpga/ecp5-spi.c
> @@ -0,0 +1,311 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Lattice ECP5 FPGA programming over slave SPI interface.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/fpga/fpga-mgr.h>
> +#include <linux/spi/spi.h>
> +
> +#define	ECP5_SPI_MAX_SPEED_HZ		60000000
> +
> +#define	ECP5_SPI_ISC_ENABLE		{0xC6, 0x00, 0x00, 0x00}
> +#define	ECP5_SPI_ISC_DISABLE		{0x26, 0x00, 0x00, 0x00}
> +#define	ECP5_SPI_ISC_ERASE		{0x0E, 0x01, 0x00, 0x00}
> +#define	ECP5_SPI_LSC_INIT_ADDR		{0x46, 0x00, 0x00, 0x00}
> +#define	ECP5_SPI_LSC_BITSTREAM_BURST	{0x7a, 0x00, 0x00, 0x00}
> +#define	ECP5_SPI_LSC_CHECK_BUSY		{0xF0, 0x00, 0x00, 0x00}
> +
> +#define ECP5_POLL_RETRIES		100000
> +
> +struct ecp5_priv {
> +	struct gpio_desc *program;
> +	struct gpio_desc *init;
> +	struct gpio_desc *done;
> +	struct spi_device *spi;
> +};
> +
> +static enum fpga_mgr_states ecp5_ops_state(struct fpga_manager *mgr)
> +{
> +	struct ecp5_priv *priv = mgr->priv;
> +
> +	return gpiod_get_value(priv->done) ? FPGA_MGR_STATE_OPERATING :
> +					     FPGA_MGR_STATE_UNKNOWN;
> +}
> +
> +static int ecp5_poll_busy(struct spi_device *spi)
> +{
> +	const u8 lsc_check_busy[] = ECP5_SPI_LSC_CHECK_BUSY;
> +	int ret, retries = ECP5_POLL_RETRIES;
> +	struct spi_transfer xfers[2] = { 0 };
> +	struct spi_message msg;
> +	u8 busy;
> +
> +	xfers[0].tx_buf = lsc_check_busy;
> +	xfers[0].len = sizeof(lsc_check_busy);
> +	xfers[1].rx_buf = &busy;
> +	xfers[1].len = sizeof(busy);
> +
> +	while (retries--) {
> +		spi_message_init_with_transfers(&msg, xfers, ARRAY_SIZE(xfers));
> +		ret = spi_sync_locked(spi, &msg);
> +		if (ret)
> +			return ret;
> +
> +		if (!busy)
> +			return 0;
> +
> +		usleep_range(50, 100);
> +	}
> +
> +	return -EBUSY;
> +}
> +
> +static int ecp5_poll_gpio(struct gpio_desc *gpio, bool is_active)
> +{
> +	int value, retries = ECP5_POLL_RETRIES;
> +
> +	while (retries--) {
> +		value = gpiod_get_value(gpio);
> +		if (value < 0)
> +			return value;
> +
> +		if ((!is_active && !value) || (is_active && value))
> +			return 0;
> +
> +		ndelay(10);
> +	}
> +
> +	return -EFAULT;
> +}
> +
> +static int ecp5_ops_write_init(struct fpga_manager *mgr,
> +			       struct fpga_image_info *info,
> +			       const char *buf, size_t count)
> +{
> +	const u8 lsc_bitstream_burst[] = ECP5_SPI_LSC_BITSTREAM_BURST;
> +	const u8 lsc_init_addr[] = ECP5_SPI_LSC_INIT_ADDR;
> +	const u8 isc_enable[] = ECP5_SPI_ISC_ENABLE;
> +	const u8 isc_erase[] = ECP5_SPI_ISC_ERASE;
> +	struct ecp5_priv *priv = mgr->priv;
> +	struct spi_device *spi = priv->spi;
> +	struct device *dev = &mgr->dev;
> +	struct spi_transfer isc_xfers[] = {
> +		{
> +			.tx_buf = isc_enable,
> +			.len = sizeof(isc_enable),
> +			.cs_change = 1,
> +		}, {
> +			.tx_buf = isc_erase,
> +			.len = sizeof(isc_erase),
> +		},
> +	};
> +	struct spi_transfer lsc_xfers[] = {
> +		{
> +			.tx_buf = lsc_init_addr,
> +			.len = sizeof(lsc_init_addr),
> +			.cs_change = 1,
> +		}, {
> +			.tx_buf = lsc_bitstream_burst,
> +			.len = sizeof(lsc_bitstream_burst),
> +			.cs_change = 1,
> +		},
> +	};
> +	struct spi_message msg;
> +	int ret;
> +
> +	if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
> +		dev_err(dev, "Partial reconfiguration is not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	/* Enter init mode */
> +	gpiod_set_value(priv->program, 1);
> +
> +	ret = ecp5_poll_gpio(priv->init, true);
> +	if (!ret)
> +		ret = ecp5_poll_gpio(priv->done, false);
> +
> +	if (ret) {
> +		dev_err(dev, "Failed to go to initialization mode\n");
> +		return ret;
> +	}
> +
> +	/* Enter program mode */
> +	gpiod_set_value(priv->program, 0);
> +
> +	ret = ecp5_poll_gpio(priv->init, false);
> +	if (ret) {
> +		dev_err(dev, "Failed to go to program mode\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * Lock SPI bus for exclusive usage until FPGA programming is done.
> +	 * SPI bus will be released in ecp5_ops_write_complete() or on error.
> +	 */
> +	spi_bus_lock(spi->controller);
> +
> +	/* Enter ISC mode */
> +	spi_message_init_with_transfers(&msg, isc_xfers, ARRAY_SIZE(isc_xfers));
> +	ret = spi_sync_locked(spi, &msg);
> +	if (!ret)
> +		ret = ecp5_poll_busy(spi);
> +
> +	if (ret) {
> +		dev_err(dev, "Failed to go to ISC mode\n");
> +		goto out;
> +	}
> +
> +	/* Prepare for bitstream burst write */
> +	spi_message_init_with_transfers(&msg, lsc_xfers, ARRAY_SIZE(lsc_xfers));
> +	ret = spi_sync_locked(spi, &msg);
> +	if (ret)
> +		dev_err(dev, "Failed to prepare for bitstream burst write\n");
> +
> +out:
> +	if (ret)
> +		spi_bus_unlock(spi->controller);
> +
> +	return ret;
> +}
> +
> +static int ecp5_ops_write(struct fpga_manager *mgr, const char *buf, size_t count)
> +{
> +	struct ecp5_priv *priv = mgr->priv;
> +	struct spi_device *spi = priv->spi;
> +	struct spi_transfer xfer = {
> +		.tx_buf = buf,
> +		.len = count,
> +		.cs_change = 1,
> +	};
> +	struct spi_message msg;
> +	int ret;
> +
> +	spi_message_init_with_transfers(&msg, &xfer, 1);
> +	ret = spi_sync_locked(spi, &msg);
> +	if (ret)
> +		spi_bus_unlock(spi->controller);
> +
> +	return ret;
> +}
> +
> +static int ecp5_ops_write_complete(struct fpga_manager *mgr,
> +				   struct fpga_image_info *info)
> +{
> +	const u8 isc_disable[] = ECP5_SPI_ISC_DISABLE;
> +	struct ecp5_priv *priv = mgr->priv;
> +	struct spi_device *spi = priv->spi;
> +	struct spi_transfer xfer = { 0 };
> +	struct device *dev = &mgr->dev;
> +	struct spi_message msg;
> +	int ret;
> +
> +	/* Toggle CS and wait for bitstream write to finish */
> +	spi_message_init_with_transfers(&msg, &xfer, 1);
> +	ret = spi_sync_locked(spi, &msg);
> +	if (!ret)
> +		ret = ecp5_poll_busy(spi);
> +
> +	if (ret) {
> +		dev_err(dev, "Error while waiting bitstream write to finish\n");
> +		goto out;
> +	}
> +
> +	/* Exit ISC mode */
> +	xfer.tx_buf = isc_disable;
> +	xfer.len = sizeof(isc_disable);
> +	spi_message_init_with_transfers(&msg, &xfer, 1);
> +	ret = spi_sync_locked(spi, &msg);
> +	if (!ret)
> +		ret = ecp5_poll_gpio(priv->done, true);
> +
> +	if (ret)
> +		dev_err(dev, "Failed to finish ISC\n");
> +
> +out:
> +	spi_bus_unlock(spi->controller);
> +
> +	return ret;
> +}
> +
> +static const struct fpga_manager_ops ecp5_fpga_ops = {
> +	.state = ecp5_ops_state,
> +	.write_init = ecp5_ops_write_init,
> +	.write = ecp5_ops_write,
> +	.write_complete = ecp5_ops_write_complete,
> +};
> +
> +static int ecp5_probe(struct spi_device *spi)
> +{
> +	struct device *dev = &spi->dev;
> +	struct fpga_manager *mgr;
> +	struct ecp5_priv *priv;
> +	int ret;
> +
> +	if (spi->max_speed_hz > ECP5_SPI_MAX_SPEED_HZ) {
> +		dev_err(dev, "SPI speed %u is too high, maximum speed is %u\n",
> +			spi->max_speed_hz, ECP5_SPI_MAX_SPEED_HZ);
> +		return -EINVAL;
> +	}
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	priv->spi = spi;
> +
> +	priv->done = devm_gpiod_get(dev, "done", GPIOD_IN);
> +	if (IS_ERR(priv->done)) {
> +		ret = PTR_ERR(priv->done);
> +		dev_err(dev, "Failed to get DONE GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	priv->init = devm_gpiod_get(dev, "init", GPIOD_IN);
> +	if (IS_ERR(priv->init)) {
> +		ret = PTR_ERR(priv->init);
> +		dev_err(dev, "Failed to get INIT GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	priv->program = devm_gpiod_get(dev, "program", GPIOD_OUT_LOW);
> +	if (IS_ERR(priv->program)) {
> +		ret = PTR_ERR(priv->program);
> +		dev_err(dev, "Failed to get PROGRAM GPIO: %d\n", ret);
> +		return ret;
> +	}
> +
> +	mgr = devm_fpga_mgr_register(dev, "Lattice ECP5 SPI FPGA Manager",
> +				     &ecp5_fpga_ops, priv);
> +
> +	return PTR_ERR_OR_ZERO(mgr);
> +}
> +
> +static const struct spi_device_id ecp5_spi_ids[] = {
> +	{ .name = "ecp5-fpga-mgr" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(spi, ecp5_spi_ids);
> +
> +#if IS_ENABLED(CONFIG_OF)
> +static const struct of_device_id ecp5_of_ids[] = {
> +	{ .compatible = "lattice,ecp5-fpga-mgr" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, ecp5_of_ids);
> +#endif /* IS_ENABLED(CONFIG_OF) */
> +
> +static struct spi_driver ecp5_driver = {
> +	.probe = ecp5_probe,
> +	.id_table = ecp5_spi_ids,
> +	.driver = {
> +		.name = "lattice_ecp5_spi_fpga_mgr",
> +		.of_match_table = of_match_ptr(ecp5_of_ids),
> +	},
> +};
> +
> +module_spi_driver(ecp5_driver);
> +
> +MODULE_DESCRIPTION("Lattice ECP5 SPI FPGA Manager");
> +MODULE_LICENSE("GPL");
> -- 
> 2.37.1
> 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 1/2] fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  2022-08-16  2:00   ` Xu Yilun
@ 2022-08-16  4:58     ` Ivan Bornyakov
  2022-08-16  6:09       ` Xu Yilun
  0 siblings, 1 reply; 9+ messages in thread
From: Ivan Bornyakov @ 2022-08-16  4:58 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, hao.wu, trix, dg, robh+dt, krzysztof.kozlowski+dt,
	linux-fpga, devicetree, linux-kernel, system

On Tue, Aug 16, 2022 at 10:00:41AM +0800, Xu Yilun wrote:
> On 2022-08-15 at 16:21:56 +0300, Ivan Bornyakov wrote:
> > Add support to the FPGA manager for programming Lattice ECP5 FPGA over
> > slave SPI interface with .bit formatted uncompressed bitstream image.
> 
> Not sure if something is missed.
> 
> https://lore.kernel.org/all/20220729145757.GA2601292@yilunxu-OptiPlex-7050/
> 
> I was considering if a generic driver for lattice slave SPI sysCONFIG
> interface could be introduced. From machxo2 & ecp5 Programming Usage
> Guide, or others in this series, they basically use the same reconfigure
> interface & protocol.
> 
> Thanks,
> Yilun
> 

I only have HW with ECP5, can't vouch for the rest.

> > 
> > Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
> > ---
> >  drivers/fpga/Kconfig    |   7 +
> >  drivers/fpga/Makefile   |   1 +
> >  drivers/fpga/ecp5-spi.c | 311 ++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 319 insertions(+)
> >  create mode 100644 drivers/fpga/ecp5-spi.c
> > 
> > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> > index 6c416955da53..920277a08ed9 100644
> > --- a/drivers/fpga/Kconfig
> > +++ b/drivers/fpga/Kconfig
> > @@ -263,4 +263,11 @@ config FPGA_MGR_MICROCHIP_SPI
> >  	  programming over slave SPI interface with .dat formatted
> >  	  bitstream image.
> >  
> > +config FPGA_MGR_ECP5_SPI
> > +	tristate "Lattice ECP5 SPI FPGA manager"
> > +	depends on SPI
> > +	help
> > +	  FPGA manager driver support for Lattice ECP5 programming over slave
> > +	  SPI interface with .bit formatted uncompressed bitstream image.
> > +
> >  endif # FPGA
> > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> > index 42ae8b58abce..17c7a3c4b385 100644
> > --- a/drivers/fpga/Makefile
> > +++ b/drivers/fpga/Makefile
> > @@ -20,6 +20,7 @@ obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA)	+= zynq-fpga.o
> >  obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA)	+= zynqmp-fpga.o
> >  obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA)	+= versal-fpga.o
> >  obj-$(CONFIG_FPGA_MGR_MICROCHIP_SPI)	+= microchip-spi.o
> > +obj-$(CONFIG_FPGA_MGR_ECP5_SPI)		+= ecp5-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
> >  
> > diff --git a/drivers/fpga/ecp5-spi.c b/drivers/fpga/ecp5-spi.c
> > new file mode 100644
> > index 000000000000..aa0dd10823a3
> > --- /dev/null
> > +++ b/drivers/fpga/ecp5-spi.c
> > @@ -0,0 +1,311 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Lattice ECP5 FPGA programming over slave SPI interface.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/fpga/fpga-mgr.h>
> > +#include <linux/spi/spi.h>
> > +
> > +#define	ECP5_SPI_MAX_SPEED_HZ		60000000
> > +
> > +#define	ECP5_SPI_ISC_ENABLE		{0xC6, 0x00, 0x00, 0x00}
> > +#define	ECP5_SPI_ISC_DISABLE		{0x26, 0x00, 0x00, 0x00}
> > +#define	ECP5_SPI_ISC_ERASE		{0x0E, 0x01, 0x00, 0x00}
> > +#define	ECP5_SPI_LSC_INIT_ADDR		{0x46, 0x00, 0x00, 0x00}
> > +#define	ECP5_SPI_LSC_BITSTREAM_BURST	{0x7a, 0x00, 0x00, 0x00}
> > +#define	ECP5_SPI_LSC_CHECK_BUSY		{0xF0, 0x00, 0x00, 0x00}
> > +
> > +#define ECP5_POLL_RETRIES		100000
> > +
> > +struct ecp5_priv {
> > +	struct gpio_desc *program;
> > +	struct gpio_desc *init;
> > +	struct gpio_desc *done;
> > +	struct spi_device *spi;
> > +};
> > +
> > +static enum fpga_mgr_states ecp5_ops_state(struct fpga_manager *mgr)
> > +{
> > +	struct ecp5_priv *priv = mgr->priv;
> > +
> > +	return gpiod_get_value(priv->done) ? FPGA_MGR_STATE_OPERATING :
> > +					     FPGA_MGR_STATE_UNKNOWN;
> > +}
> > +
> > +static int ecp5_poll_busy(struct spi_device *spi)
> > +{
> > +	const u8 lsc_check_busy[] = ECP5_SPI_LSC_CHECK_BUSY;
> > +	int ret, retries = ECP5_POLL_RETRIES;
> > +	struct spi_transfer xfers[2] = { 0 };
> > +	struct spi_message msg;
> > +	u8 busy;
> > +
> > +	xfers[0].tx_buf = lsc_check_busy;
> > +	xfers[0].len = sizeof(lsc_check_busy);
> > +	xfers[1].rx_buf = &busy;
> > +	xfers[1].len = sizeof(busy);
> > +
> > +	while (retries--) {
> > +		spi_message_init_with_transfers(&msg, xfers, ARRAY_SIZE(xfers));
> > +		ret = spi_sync_locked(spi, &msg);
> > +		if (ret)
> > +			return ret;
> > +
> > +		if (!busy)
> > +			return 0;
> > +
> > +		usleep_range(50, 100);
> > +	}
> > +
> > +	return -EBUSY;
> > +}
> > +
> > +static int ecp5_poll_gpio(struct gpio_desc *gpio, bool is_active)
> > +{
> > +	int value, retries = ECP5_POLL_RETRIES;
> > +
> > +	while (retries--) {
> > +		value = gpiod_get_value(gpio);
> > +		if (value < 0)
> > +			return value;
> > +
> > +		if ((!is_active && !value) || (is_active && value))
> > +			return 0;
> > +
> > +		ndelay(10);
> > +	}
> > +
> > +	return -EFAULT;
> > +}
> > +
> > +static int ecp5_ops_write_init(struct fpga_manager *mgr,
> > +			       struct fpga_image_info *info,
> > +			       const char *buf, size_t count)
> > +{
> > +	const u8 lsc_bitstream_burst[] = ECP5_SPI_LSC_BITSTREAM_BURST;
> > +	const u8 lsc_init_addr[] = ECP5_SPI_LSC_INIT_ADDR;
> > +	const u8 isc_enable[] = ECP5_SPI_ISC_ENABLE;
> > +	const u8 isc_erase[] = ECP5_SPI_ISC_ERASE;
> > +	struct ecp5_priv *priv = mgr->priv;
> > +	struct spi_device *spi = priv->spi;
> > +	struct device *dev = &mgr->dev;
> > +	struct spi_transfer isc_xfers[] = {
> > +		{
> > +			.tx_buf = isc_enable,
> > +			.len = sizeof(isc_enable),
> > +			.cs_change = 1,
> > +		}, {
> > +			.tx_buf = isc_erase,
> > +			.len = sizeof(isc_erase),
> > +		},
> > +	};
> > +	struct spi_transfer lsc_xfers[] = {
> > +		{
> > +			.tx_buf = lsc_init_addr,
> > +			.len = sizeof(lsc_init_addr),
> > +			.cs_change = 1,
> > +		}, {
> > +			.tx_buf = lsc_bitstream_burst,
> > +			.len = sizeof(lsc_bitstream_burst),
> > +			.cs_change = 1,
> > +		},
> > +	};
> > +	struct spi_message msg;
> > +	int ret;
> > +
> > +	if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
> > +		dev_err(dev, "Partial reconfiguration is not supported\n");
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	/* Enter init mode */
> > +	gpiod_set_value(priv->program, 1);
> > +
> > +	ret = ecp5_poll_gpio(priv->init, true);
> > +	if (!ret)
> > +		ret = ecp5_poll_gpio(priv->done, false);
> > +
> > +	if (ret) {
> > +		dev_err(dev, "Failed to go to initialization mode\n");
> > +		return ret;
> > +	}
> > +
> > +	/* Enter program mode */
> > +	gpiod_set_value(priv->program, 0);
> > +
> > +	ret = ecp5_poll_gpio(priv->init, false);
> > +	if (ret) {
> > +		dev_err(dev, "Failed to go to program mode\n");
> > +		return ret;
> > +	}
> > +
> > +	/*
> > +	 * Lock SPI bus for exclusive usage until FPGA programming is done.
> > +	 * SPI bus will be released in ecp5_ops_write_complete() or on error.
> > +	 */
> > +	spi_bus_lock(spi->controller);
> > +
> > +	/* Enter ISC mode */
> > +	spi_message_init_with_transfers(&msg, isc_xfers, ARRAY_SIZE(isc_xfers));
> > +	ret = spi_sync_locked(spi, &msg);
> > +	if (!ret)
> > +		ret = ecp5_poll_busy(spi);
> > +
> > +	if (ret) {
> > +		dev_err(dev, "Failed to go to ISC mode\n");
> > +		goto out;
> > +	}
> > +
> > +	/* Prepare for bitstream burst write */
> > +	spi_message_init_with_transfers(&msg, lsc_xfers, ARRAY_SIZE(lsc_xfers));
> > +	ret = spi_sync_locked(spi, &msg);
> > +	if (ret)
> > +		dev_err(dev, "Failed to prepare for bitstream burst write\n");
> > +
> > +out:
> > +	if (ret)
> > +		spi_bus_unlock(spi->controller);
> > +
> > +	return ret;
> > +}
> > +
> > +static int ecp5_ops_write(struct fpga_manager *mgr, const char *buf, size_t count)
> > +{
> > +	struct ecp5_priv *priv = mgr->priv;
> > +	struct spi_device *spi = priv->spi;
> > +	struct spi_transfer xfer = {
> > +		.tx_buf = buf,
> > +		.len = count,
> > +		.cs_change = 1,
> > +	};
> > +	struct spi_message msg;
> > +	int ret;
> > +
> > +	spi_message_init_with_transfers(&msg, &xfer, 1);
> > +	ret = spi_sync_locked(spi, &msg);
> > +	if (ret)
> > +		spi_bus_unlock(spi->controller);
> > +
> > +	return ret;
> > +}
> > +
> > +static int ecp5_ops_write_complete(struct fpga_manager *mgr,
> > +				   struct fpga_image_info *info)
> > +{
> > +	const u8 isc_disable[] = ECP5_SPI_ISC_DISABLE;
> > +	struct ecp5_priv *priv = mgr->priv;
> > +	struct spi_device *spi = priv->spi;
> > +	struct spi_transfer xfer = { 0 };
> > +	struct device *dev = &mgr->dev;
> > +	struct spi_message msg;
> > +	int ret;
> > +
> > +	/* Toggle CS and wait for bitstream write to finish */
> > +	spi_message_init_with_transfers(&msg, &xfer, 1);
> > +	ret = spi_sync_locked(spi, &msg);
> > +	if (!ret)
> > +		ret = ecp5_poll_busy(spi);
> > +
> > +	if (ret) {
> > +		dev_err(dev, "Error while waiting bitstream write to finish\n");
> > +		goto out;
> > +	}
> > +
> > +	/* Exit ISC mode */
> > +	xfer.tx_buf = isc_disable;
> > +	xfer.len = sizeof(isc_disable);
> > +	spi_message_init_with_transfers(&msg, &xfer, 1);
> > +	ret = spi_sync_locked(spi, &msg);
> > +	if (!ret)
> > +		ret = ecp5_poll_gpio(priv->done, true);
> > +
> > +	if (ret)
> > +		dev_err(dev, "Failed to finish ISC\n");
> > +
> > +out:
> > +	spi_bus_unlock(spi->controller);
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct fpga_manager_ops ecp5_fpga_ops = {
> > +	.state = ecp5_ops_state,
> > +	.write_init = ecp5_ops_write_init,
> > +	.write = ecp5_ops_write,
> > +	.write_complete = ecp5_ops_write_complete,
> > +};
> > +
> > +static int ecp5_probe(struct spi_device *spi)
> > +{
> > +	struct device *dev = &spi->dev;
> > +	struct fpga_manager *mgr;
> > +	struct ecp5_priv *priv;
> > +	int ret;
> > +
> > +	if (spi->max_speed_hz > ECP5_SPI_MAX_SPEED_HZ) {
> > +		dev_err(dev, "SPI speed %u is too high, maximum speed is %u\n",
> > +			spi->max_speed_hz, ECP5_SPI_MAX_SPEED_HZ);
> > +		return -EINVAL;
> > +	}
> > +
> > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > +	if (!priv)
> > +		return -ENOMEM;
> > +
> > +	priv->spi = spi;
> > +
> > +	priv->done = devm_gpiod_get(dev, "done", GPIOD_IN);
> > +	if (IS_ERR(priv->done)) {
> > +		ret = PTR_ERR(priv->done);
> > +		dev_err(dev, "Failed to get DONE GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	priv->init = devm_gpiod_get(dev, "init", GPIOD_IN);
> > +	if (IS_ERR(priv->init)) {
> > +		ret = PTR_ERR(priv->init);
> > +		dev_err(dev, "Failed to get INIT GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	priv->program = devm_gpiod_get(dev, "program", GPIOD_OUT_LOW);
> > +	if (IS_ERR(priv->program)) {
> > +		ret = PTR_ERR(priv->program);
> > +		dev_err(dev, "Failed to get PROGRAM GPIO: %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	mgr = devm_fpga_mgr_register(dev, "Lattice ECP5 SPI FPGA Manager",
> > +				     &ecp5_fpga_ops, priv);
> > +
> > +	return PTR_ERR_OR_ZERO(mgr);
> > +}
> > +
> > +static const struct spi_device_id ecp5_spi_ids[] = {
> > +	{ .name = "ecp5-fpga-mgr" },
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(spi, ecp5_spi_ids);
> > +
> > +#if IS_ENABLED(CONFIG_OF)
> > +static const struct of_device_id ecp5_of_ids[] = {
> > +	{ .compatible = "lattice,ecp5-fpga-mgr" },
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(of, ecp5_of_ids);
> > +#endif /* IS_ENABLED(CONFIG_OF) */
> > +
> > +static struct spi_driver ecp5_driver = {
> > +	.probe = ecp5_probe,
> > +	.id_table = ecp5_spi_ids,
> > +	.driver = {
> > +		.name = "lattice_ecp5_spi_fpga_mgr",
> > +		.of_match_table = of_match_ptr(ecp5_of_ids),
> > +	},
> > +};
> > +
> > +module_spi_driver(ecp5_driver);
> > +
> > +MODULE_DESCRIPTION("Lattice ECP5 SPI FPGA Manager");
> > +MODULE_LICENSE("GPL");
> > -- 
> > 2.37.1
> > 
> > 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 1/2] fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  2022-08-16  4:58     ` Ivan Bornyakov
@ 2022-08-16  6:09       ` Xu Yilun
  2022-08-16  7:42         ` Daniel Glöckner
  2022-08-17  9:04         ` Ivan Bornyakov
  0 siblings, 2 replies; 9+ messages in thread
From: Xu Yilun @ 2022-08-16  6:09 UTC (permalink / raw)
  To: Ivan Bornyakov
  Cc: mdf, hao.wu, trix, dg, robh+dt, krzysztof.kozlowski+dt,
	linux-fpga, devicetree, linux-kernel, system, yilun.xu

On 2022-08-16 at 07:58:41 +0300, Ivan Bornyakov wrote:
> On Tue, Aug 16, 2022 at 10:00:41AM +0800, Xu Yilun wrote:
> > On 2022-08-15 at 16:21:56 +0300, Ivan Bornyakov wrote:
> > > Add support to the FPGA manager for programming Lattice ECP5 FPGA over
> > > slave SPI interface with .bit formatted uncompressed bitstream image.
> > 
> > Not sure if something is missed.
> > 
> > https://lore.kernel.org/all/20220729145757.GA2601292@yilunxu-OptiPlex-7050/
> > 
> > I was considering if a generic driver for lattice slave SPI sysCONFIG
> > interface could be introduced. From machxo2 & ecp5 Programming Usage
> > Guide, or others in this series, they basically use the same reconfigure
> > interface & protocol.
> > 
> > Thanks,
> > Yilun
> > 
> 
> I only have HW with ECP5, can't vouch for the rest.

I understand your concern, but having separate drivers for the same IP on
different boards makes the maintaining harder.

We don't have to make everything fine, but start with machxo2 and ecp5
first. If the change affects machxo2, other people may help.

Thanks,
Yilun

> 
> > > 
> > > Signed-off-by: Ivan Bornyakov <i.bornyakov@metrotek.ru>
> > > ---
> > >  drivers/fpga/Kconfig    |   7 +
> > >  drivers/fpga/Makefile   |   1 +
> > >  drivers/fpga/ecp5-spi.c | 311 ++++++++++++++++++++++++++++++++++++++++
> > >  3 files changed, 319 insertions(+)
> > >  create mode 100644 drivers/fpga/ecp5-spi.c
> > > 
> > > diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
> > > index 6c416955da53..920277a08ed9 100644
> > > --- a/drivers/fpga/Kconfig
> > > +++ b/drivers/fpga/Kconfig
> > > @@ -263,4 +263,11 @@ config FPGA_MGR_MICROCHIP_SPI
> > >  	  programming over slave SPI interface with .dat formatted
> > >  	  bitstream image.
> > >  
> > > +config FPGA_MGR_ECP5_SPI
> > > +	tristate "Lattice ECP5 SPI FPGA manager"
> > > +	depends on SPI
> > > +	help
> > > +	  FPGA manager driver support for Lattice ECP5 programming over slave
> > > +	  SPI interface with .bit formatted uncompressed bitstream image.
> > > +
> > >  endif # FPGA
> > > diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
> > > index 42ae8b58abce..17c7a3c4b385 100644
> > > --- a/drivers/fpga/Makefile
> > > +++ b/drivers/fpga/Makefile
> > > @@ -20,6 +20,7 @@ obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA)	+= zynq-fpga.o
> > >  obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA)	+= zynqmp-fpga.o
> > >  obj-$(CONFIG_FPGA_MGR_VERSAL_FPGA)	+= versal-fpga.o
> > >  obj-$(CONFIG_FPGA_MGR_MICROCHIP_SPI)	+= microchip-spi.o
> > > +obj-$(CONFIG_FPGA_MGR_ECP5_SPI)		+= ecp5-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
> > >  
> > > diff --git a/drivers/fpga/ecp5-spi.c b/drivers/fpga/ecp5-spi.c
> > > new file mode 100644
> > > index 000000000000..aa0dd10823a3
> > > --- /dev/null
> > > +++ b/drivers/fpga/ecp5-spi.c
> > > @@ -0,0 +1,311 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Lattice ECP5 FPGA programming over slave SPI interface.
> > > + */
> > > +
> > > +#include <linux/delay.h>
> > > +#include <linux/fpga/fpga-mgr.h>
> > > +#include <linux/spi/spi.h>
> > > +
> > > +#define	ECP5_SPI_MAX_SPEED_HZ		60000000
> > > +
> > > +#define	ECP5_SPI_ISC_ENABLE		{0xC6, 0x00, 0x00, 0x00}
> > > +#define	ECP5_SPI_ISC_DISABLE		{0x26, 0x00, 0x00, 0x00}
> > > +#define	ECP5_SPI_ISC_ERASE		{0x0E, 0x01, 0x00, 0x00}
> > > +#define	ECP5_SPI_LSC_INIT_ADDR		{0x46, 0x00, 0x00, 0x00}
> > > +#define	ECP5_SPI_LSC_BITSTREAM_BURST	{0x7a, 0x00, 0x00, 0x00}
> > > +#define	ECP5_SPI_LSC_CHECK_BUSY		{0xF0, 0x00, 0x00, 0x00}
> > > +
> > > +#define ECP5_POLL_RETRIES		100000
> > > +
> > > +struct ecp5_priv {
> > > +	struct gpio_desc *program;
> > > +	struct gpio_desc *init;
> > > +	struct gpio_desc *done;
> > > +	struct spi_device *spi;
> > > +};
> > > +
> > > +static enum fpga_mgr_states ecp5_ops_state(struct fpga_manager *mgr)
> > > +{
> > > +	struct ecp5_priv *priv = mgr->priv;
> > > +
> > > +	return gpiod_get_value(priv->done) ? FPGA_MGR_STATE_OPERATING :
> > > +					     FPGA_MGR_STATE_UNKNOWN;
> > > +}
> > > +
> > > +static int ecp5_poll_busy(struct spi_device *spi)
> > > +{
> > > +	const u8 lsc_check_busy[] = ECP5_SPI_LSC_CHECK_BUSY;
> > > +	int ret, retries = ECP5_POLL_RETRIES;
> > > +	struct spi_transfer xfers[2] = { 0 };
> > > +	struct spi_message msg;
> > > +	u8 busy;
> > > +
> > > +	xfers[0].tx_buf = lsc_check_busy;
> > > +	xfers[0].len = sizeof(lsc_check_busy);
> > > +	xfers[1].rx_buf = &busy;
> > > +	xfers[1].len = sizeof(busy);
> > > +
> > > +	while (retries--) {
> > > +		spi_message_init_with_transfers(&msg, xfers, ARRAY_SIZE(xfers));
> > > +		ret = spi_sync_locked(spi, &msg);
> > > +		if (ret)
> > > +			return ret;
> > > +
> > > +		if (!busy)
> > > +			return 0;
> > > +
> > > +		usleep_range(50, 100);
> > > +	}
> > > +
> > > +	return -EBUSY;
> > > +}
> > > +
> > > +static int ecp5_poll_gpio(struct gpio_desc *gpio, bool is_active)
> > > +{
> > > +	int value, retries = ECP5_POLL_RETRIES;
> > > +
> > > +	while (retries--) {
> > > +		value = gpiod_get_value(gpio);
> > > +		if (value < 0)
> > > +			return value;
> > > +
> > > +		if ((!is_active && !value) || (is_active && value))
> > > +			return 0;
> > > +
> > > +		ndelay(10);
> > > +	}
> > > +
> > > +	return -EFAULT;
> > > +}
> > > +
> > > +static int ecp5_ops_write_init(struct fpga_manager *mgr,
> > > +			       struct fpga_image_info *info,
> > > +			       const char *buf, size_t count)
> > > +{
> > > +	const u8 lsc_bitstream_burst[] = ECP5_SPI_LSC_BITSTREAM_BURST;
> > > +	const u8 lsc_init_addr[] = ECP5_SPI_LSC_INIT_ADDR;
> > > +	const u8 isc_enable[] = ECP5_SPI_ISC_ENABLE;
> > > +	const u8 isc_erase[] = ECP5_SPI_ISC_ERASE;
> > > +	struct ecp5_priv *priv = mgr->priv;
> > > +	struct spi_device *spi = priv->spi;
> > > +	struct device *dev = &mgr->dev;
> > > +	struct spi_transfer isc_xfers[] = {
> > > +		{
> > > +			.tx_buf = isc_enable,
> > > +			.len = sizeof(isc_enable),
> > > +			.cs_change = 1,
> > > +		}, {
> > > +			.tx_buf = isc_erase,
> > > +			.len = sizeof(isc_erase),
> > > +		},
> > > +	};
> > > +	struct spi_transfer lsc_xfers[] = {
> > > +		{
> > > +			.tx_buf = lsc_init_addr,
> > > +			.len = sizeof(lsc_init_addr),
> > > +			.cs_change = 1,
> > > +		}, {
> > > +			.tx_buf = lsc_bitstream_burst,
> > > +			.len = sizeof(lsc_bitstream_burst),
> > > +			.cs_change = 1,
> > > +		},
> > > +	};
> > > +	struct spi_message msg;
> > > +	int ret;
> > > +
> > > +	if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
> > > +		dev_err(dev, "Partial reconfiguration is not supported\n");
> > > +		return -EOPNOTSUPP;
> > > +	}
> > > +
> > > +	/* Enter init mode */
> > > +	gpiod_set_value(priv->program, 1);
> > > +
> > > +	ret = ecp5_poll_gpio(priv->init, true);
> > > +	if (!ret)
> > > +		ret = ecp5_poll_gpio(priv->done, false);
> > > +
> > > +	if (ret) {
> > > +		dev_err(dev, "Failed to go to initialization mode\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	/* Enter program mode */
> > > +	gpiod_set_value(priv->program, 0);
> > > +
> > > +	ret = ecp5_poll_gpio(priv->init, false);
> > > +	if (ret) {
> > > +		dev_err(dev, "Failed to go to program mode\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	/*
> > > +	 * Lock SPI bus for exclusive usage until FPGA programming is done.
> > > +	 * SPI bus will be released in ecp5_ops_write_complete() or on error.
> > > +	 */
> > > +	spi_bus_lock(spi->controller);
> > > +
> > > +	/* Enter ISC mode */
> > > +	spi_message_init_with_transfers(&msg, isc_xfers, ARRAY_SIZE(isc_xfers));
> > > +	ret = spi_sync_locked(spi, &msg);
> > > +	if (!ret)
> > > +		ret = ecp5_poll_busy(spi);
> > > +
> > > +	if (ret) {
> > > +		dev_err(dev, "Failed to go to ISC mode\n");
> > > +		goto out;
> > > +	}
> > > +
> > > +	/* Prepare for bitstream burst write */
> > > +	spi_message_init_with_transfers(&msg, lsc_xfers, ARRAY_SIZE(lsc_xfers));
> > > +	ret = spi_sync_locked(spi, &msg);
> > > +	if (ret)
> > > +		dev_err(dev, "Failed to prepare for bitstream burst write\n");
> > > +
> > > +out:
> > > +	if (ret)
> > > +		spi_bus_unlock(spi->controller);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int ecp5_ops_write(struct fpga_manager *mgr, const char *buf, size_t count)
> > > +{
> > > +	struct ecp5_priv *priv = mgr->priv;
> > > +	struct spi_device *spi = priv->spi;
> > > +	struct spi_transfer xfer = {
> > > +		.tx_buf = buf,
> > > +		.len = count,
> > > +		.cs_change = 1,
> > > +	};
> > > +	struct spi_message msg;
> > > +	int ret;
> > > +
> > > +	spi_message_init_with_transfers(&msg, &xfer, 1);
> > > +	ret = spi_sync_locked(spi, &msg);
> > > +	if (ret)
> > > +		spi_bus_unlock(spi->controller);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int ecp5_ops_write_complete(struct fpga_manager *mgr,
> > > +				   struct fpga_image_info *info)
> > > +{
> > > +	const u8 isc_disable[] = ECP5_SPI_ISC_DISABLE;
> > > +	struct ecp5_priv *priv = mgr->priv;
> > > +	struct spi_device *spi = priv->spi;
> > > +	struct spi_transfer xfer = { 0 };
> > > +	struct device *dev = &mgr->dev;
> > > +	struct spi_message msg;
> > > +	int ret;
> > > +
> > > +	/* Toggle CS and wait for bitstream write to finish */
> > > +	spi_message_init_with_transfers(&msg, &xfer, 1);
> > > +	ret = spi_sync_locked(spi, &msg);
> > > +	if (!ret)
> > > +		ret = ecp5_poll_busy(spi);
> > > +
> > > +	if (ret) {
> > > +		dev_err(dev, "Error while waiting bitstream write to finish\n");
> > > +		goto out;
> > > +	}
> > > +
> > > +	/* Exit ISC mode */
> > > +	xfer.tx_buf = isc_disable;
> > > +	xfer.len = sizeof(isc_disable);
> > > +	spi_message_init_with_transfers(&msg, &xfer, 1);
> > > +	ret = spi_sync_locked(spi, &msg);
> > > +	if (!ret)
> > > +		ret = ecp5_poll_gpio(priv->done, true);
> > > +
> > > +	if (ret)
> > > +		dev_err(dev, "Failed to finish ISC\n");
> > > +
> > > +out:
> > > +	spi_bus_unlock(spi->controller);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static const struct fpga_manager_ops ecp5_fpga_ops = {
> > > +	.state = ecp5_ops_state,
> > > +	.write_init = ecp5_ops_write_init,
> > > +	.write = ecp5_ops_write,
> > > +	.write_complete = ecp5_ops_write_complete,
> > > +};
> > > +
> > > +static int ecp5_probe(struct spi_device *spi)
> > > +{
> > > +	struct device *dev = &spi->dev;
> > > +	struct fpga_manager *mgr;
> > > +	struct ecp5_priv *priv;
> > > +	int ret;
> > > +
> > > +	if (spi->max_speed_hz > ECP5_SPI_MAX_SPEED_HZ) {
> > > +		dev_err(dev, "SPI speed %u is too high, maximum speed is %u\n",
> > > +			spi->max_speed_hz, ECP5_SPI_MAX_SPEED_HZ);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > > +	if (!priv)
> > > +		return -ENOMEM;
> > > +
> > > +	priv->spi = spi;
> > > +
> > > +	priv->done = devm_gpiod_get(dev, "done", GPIOD_IN);
> > > +	if (IS_ERR(priv->done)) {
> > > +		ret = PTR_ERR(priv->done);
> > > +		dev_err(dev, "Failed to get DONE GPIO: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	priv->init = devm_gpiod_get(dev, "init", GPIOD_IN);
> > > +	if (IS_ERR(priv->init)) {
> > > +		ret = PTR_ERR(priv->init);
> > > +		dev_err(dev, "Failed to get INIT GPIO: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	priv->program = devm_gpiod_get(dev, "program", GPIOD_OUT_LOW);
> > > +	if (IS_ERR(priv->program)) {
> > > +		ret = PTR_ERR(priv->program);
> > > +		dev_err(dev, "Failed to get PROGRAM GPIO: %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	mgr = devm_fpga_mgr_register(dev, "Lattice ECP5 SPI FPGA Manager",
> > > +				     &ecp5_fpga_ops, priv);
> > > +
> > > +	return PTR_ERR_OR_ZERO(mgr);
> > > +}
> > > +
> > > +static const struct spi_device_id ecp5_spi_ids[] = {
> > > +	{ .name = "ecp5-fpga-mgr" },
> > > +	{},
> > > +};
> > > +MODULE_DEVICE_TABLE(spi, ecp5_spi_ids);
> > > +
> > > +#if IS_ENABLED(CONFIG_OF)
> > > +static const struct of_device_id ecp5_of_ids[] = {
> > > +	{ .compatible = "lattice,ecp5-fpga-mgr" },
> > > +	{},
> > > +};
> > > +MODULE_DEVICE_TABLE(of, ecp5_of_ids);
> > > +#endif /* IS_ENABLED(CONFIG_OF) */
> > > +
> > > +static struct spi_driver ecp5_driver = {
> > > +	.probe = ecp5_probe,
> > > +	.id_table = ecp5_spi_ids,
> > > +	.driver = {
> > > +		.name = "lattice_ecp5_spi_fpga_mgr",
> > > +		.of_match_table = of_match_ptr(ecp5_of_ids),
> > > +	},
> > > +};
> > > +
> > > +module_spi_driver(ecp5_driver);
> > > +
> > > +MODULE_DESCRIPTION("Lattice ECP5 SPI FPGA Manager");
> > > +MODULE_LICENSE("GPL");
> > > -- 
> > > 2.37.1
> > > 
> > > 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 1/2] fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  2022-08-16  6:09       ` Xu Yilun
@ 2022-08-16  7:42         ` Daniel Glöckner
  2022-08-17  9:04         ` Ivan Bornyakov
  1 sibling, 0 replies; 9+ messages in thread
From: Daniel Glöckner @ 2022-08-16  7:42 UTC (permalink / raw)
  To: Xu Yilun
  Cc: Ivan Bornyakov, mdf, hao.wu, trix, robh+dt,
	krzysztof.kozlowski+dt, linux-fpga, devicetree, linux-kernel,
	system

On Tue, Aug 16, 2022 at 02:09:18PM +0800, Xu Yilun wrote:
> We don't have to make everything fine, but start with machxo2 and ecp5
> first. If the change affects machxo2, other people may help.

Programming MachXO* chips uses different sequences of commands. With ECP5
you put the chip into configuration mode and then upload the bitstream
into RAM cells. With MachXO chips you write the bitstream to non-volatile
storage and then tell the chip to go into configuration mode where it
automatically loads the bitstream from non-volatile storage. There is no
way to directly write the RAM cells.

Best regards,

  Daniel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 1/2] fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  2022-08-16  6:09       ` Xu Yilun
  2022-08-16  7:42         ` Daniel Glöckner
@ 2022-08-17  9:04         ` Ivan Bornyakov
  2022-08-18  2:13           ` Xu Yilun
  1 sibling, 1 reply; 9+ messages in thread
From: Ivan Bornyakov @ 2022-08-17  9:04 UTC (permalink / raw)
  To: Xu Yilun
  Cc: mdf, hao.wu, trix, dg, robh+dt, krzysztof.kozlowski+dt,
	linux-fpga, devicetree, linux-kernel, system

On Tue, Aug 16, 2022 at 02:09:18PM +0800, Xu Yilun wrote:
> On 2022-08-16 at 07:58:41 +0300, Ivan Bornyakov wrote:
> > On Tue, Aug 16, 2022 at 10:00:41AM +0800, Xu Yilun wrote:
> > > On 2022-08-15 at 16:21:56 +0300, Ivan Bornyakov wrote:
> > > > Add support to the FPGA manager for programming Lattice ECP5 FPGA over
> > > > slave SPI interface with .bit formatted uncompressed bitstream image.
> > > 
> > > Not sure if something is missed.
> > > 
> > > https://lore.kernel.org/all/20220729145757.GA2601292@yilunxu-OptiPlex-7050/
> > > 
> > > I was considering if a generic driver for lattice slave SPI sysCONFIG
> > > interface could be introduced. From machxo2 & ecp5 Programming Usage
> > > Guide, or others in this series, they basically use the same reconfigure
> > > interface & protocol.
> > > 
> > > Thanks,
> > > Yilun
> > > 
> > 
> > I only have HW with ECP5, can't vouch for the rest.
> 
> I understand your concern, but having separate drivers for the same IP on
> different boards makes the maintaining harder.
> 
> We don't have to make everything fine, but start with machxo2 and ecp5
> first. If the change affects machxo2, other people may help.
> 
> Thanks,
> Yilun
> 

Hi, Yilun.

To clear things out, considering what Daniel said in
https://lore.kernel.org/linux-fpga/20220816074216.GA31706@homes.emlix.com/
you still don't want separate ECP5 driver, or this v6 is OK and review
continues?


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v6 1/2] fpga: ecp5-spi: add Lattice ECP5 FPGA manager
  2022-08-17  9:04         ` Ivan Bornyakov
@ 2022-08-18  2:13           ` Xu Yilun
  0 siblings, 0 replies; 9+ messages in thread
From: Xu Yilun @ 2022-08-18  2:13 UTC (permalink / raw)
  To: Ivan Bornyakov
  Cc: mdf, hao.wu, trix, dg, robh+dt, krzysztof.kozlowski+dt,
	linux-fpga, devicetree, linux-kernel, system, yilun.xu

On 2022-08-17 at 12:04:13 +0300, Ivan Bornyakov wrote:
> On Tue, Aug 16, 2022 at 02:09:18PM +0800, Xu Yilun wrote:
> > On 2022-08-16 at 07:58:41 +0300, Ivan Bornyakov wrote:
> > > On Tue, Aug 16, 2022 at 10:00:41AM +0800, Xu Yilun wrote:
> > > > On 2022-08-15 at 16:21:56 +0300, Ivan Bornyakov wrote:
> > > > > Add support to the FPGA manager for programming Lattice ECP5 FPGA over
> > > > > slave SPI interface with .bit formatted uncompressed bitstream image.
> > > > 
> > > > Not sure if something is missed.
> > > > 
> > > > https://lore.kernel.org/all/20220729145757.GA2601292@yilunxu-OptiPlex-7050/
> > > > 
> > > > I was considering if a generic driver for lattice slave SPI sysCONFIG
> > > > interface could be introduced. From machxo2 & ecp5 Programming Usage
> > > > Guide, or others in this series, they basically use the same reconfigure
> > > > interface & protocol.
> > > > 
> > > > Thanks,
> > > > Yilun
> > > > 
> > > 
> > > I only have HW with ECP5, can't vouch for the rest.
> > 
> > I understand your concern, but having separate drivers for the same IP on
> > different boards makes the maintaining harder.
> > 
> > We don't have to make everything fine, but start with machxo2 and ecp5
> > first. If the change affects machxo2, other people may help.
> > 
> > Thanks,
> > Yilun
> > 
> 
> Hi, Yilun.
> 
> To clear things out, considering what Daniel said in
> https://lore.kernel.org/linux-fpga/20220816074216.GA31706@homes.emlix.com/
> you still don't want separate ECP5 driver, or this v6 is OK and review

I still perfer one sysCONFIG driver. If I understand right, the 2
reprogramming modes and the command sets are within the scope of
sysCONFIG spec. So they could be all supported in sysCONFIG driver, and
I assume some config option could be made to adapt different boards.

Thanks,
Yilun

> continues?
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2022-08-18  2:22 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-15 13:21 [PATCH v6 0/2] Lattice ECP5 FPGA manager Ivan Bornyakov
2022-08-15 13:21 ` [PATCH v6 1/2] fpga: ecp5-spi: add " Ivan Bornyakov
2022-08-16  2:00   ` Xu Yilun
2022-08-16  4:58     ` Ivan Bornyakov
2022-08-16  6:09       ` Xu Yilun
2022-08-16  7:42         ` Daniel Glöckner
2022-08-17  9:04         ` Ivan Bornyakov
2022-08-18  2:13           ` Xu Yilun
2022-08-15 13:21 ` [PATCH v6 2/2] dt-bindings: fpga: add binding doc for ecp5-spi fpga mgr Ivan Bornyakov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.