FPGA Manager changes for 6.2-rc1
Zynq: - Yang's change Switch to use dev_err_probe() helper Lattice: - Ivan's change add support for Lattice Sysconfig FPGA reprogrammer 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----- iIkEABYIADEWIQSgSJpClIeaArXyudb8twOBpKCM2gUCY3Od2BMceWlsdW4ueHVA aW50ZWwuY29tAAoJEPy3A4GkoIzaxEsBALXvny61hdQExwrlo0E27x30udYItQ6k VI6rH6wJFO1mAP9Oims7qmS0hBWdMb88I1mUYqkYA+kA0qSUbFFl4cTVCg== =yxAg -----END PGP SIGNATURE----- Merge tag 'fpga-for-6.2-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.2-rc1 Zynq: - Yang's change Switch to use dev_err_probe() helper Lattice: - Ivan's change add support for Lattice Sysconfig FPGA reprogrammer 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-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/fpga/linux-fpga: dt-bindings: fpga: document Lattice sysCONFIG FPGA manager fpga: lattice-sysconfig-spi: add Lattice sysCONFIG FPGA manager fpga: zynq: Switch to use dev_err_probe() helper
This commit is contained in:
commit
e246e4691f
|
@ -0,0 +1,81 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/fpga/lattice,sysconfig.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Lattice Slave SPI sysCONFIG FPGA manager
|
||||
|
||||
maintainers:
|
||||
- Ivan Bornyakov <i.bornyakov@metrotek.ru>
|
||||
|
||||
description: |
|
||||
Lattice sysCONFIG port, which is used for FPGA configuration, among others,
|
||||
have Slave Serial Peripheral Interface. Only full reconfiguration is
|
||||
supported.
|
||||
|
||||
Programming of ECP5 is done by writing uncompressed bitstream image in .bit
|
||||
format into FPGA's SRAM configuration memory.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- lattice,sysconfig-ecp5
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
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
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: lattice,sysconfig-ecp5
|
||||
then:
|
||||
properties:
|
||||
spi-max-frequency:
|
||||
maximum: 60000000
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
fpga-mgr@0 {
|
||||
compatible = "lattice,sysconfig-ecp5";
|
||||
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>;
|
||||
};
|
||||
};
|
|
@ -263,4 +263,15 @@ config FPGA_MGR_MICROCHIP_SPI
|
|||
programming over slave SPI interface with .dat formatted
|
||||
bitstream image.
|
||||
|
||||
config FPGA_MGR_LATTICE_SYSCONFIG
|
||||
tristate
|
||||
|
||||
config FPGA_MGR_LATTICE_SYSCONFIG_SPI
|
||||
tristate "Lattice sysCONFIG SPI FPGA manager"
|
||||
depends on SPI
|
||||
select FPGA_MGR_LATTICE_SYSCONFIG
|
||||
help
|
||||
FPGA manager driver support for Lattice FPGAs programming over slave
|
||||
SPI sysCONFIG interface.
|
||||
|
||||
endif # FPGA
|
||||
|
|
|
@ -20,6 +20,8 @@ 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_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
|
||||
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Lattice FPGA programming over slave SPI sysCONFIG interface.
|
||||
*/
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "lattice-sysconfig.h"
|
||||
|
||||
static const u32 ecp5_spi_max_speed_hz = 60000000;
|
||||
|
||||
static int sysconfig_spi_cmd_transfer(struct sysconfig_priv *priv,
|
||||
const void *tx_buf, size_t tx_len,
|
||||
void *rx_buf, size_t rx_len)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(priv->dev);
|
||||
|
||||
return spi_write_then_read(spi, tx_buf, tx_len, rx_buf, rx_len);
|
||||
}
|
||||
|
||||
static int sysconfig_spi_bitstream_burst_init(struct sysconfig_priv *priv)
|
||||
{
|
||||
const u8 lsc_bitstream_burst[] = SYSCONFIG_LSC_BITSTREAM_BURST;
|
||||
struct spi_device *spi = to_spi_device(priv->dev);
|
||||
struct spi_transfer xfer = {};
|
||||
struct spi_message msg;
|
||||
size_t buf_len;
|
||||
void *buf;
|
||||
int ret;
|
||||
|
||||
buf_len = sizeof(lsc_bitstream_burst);
|
||||
|
||||
buf = kmemdup(lsc_bitstream_burst, buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
xfer.len = buf_len;
|
||||
xfer.tx_buf = buf;
|
||||
xfer.cs_change = 1;
|
||||
|
||||
spi_message_init_with_transfers(&msg, &xfer, 1);
|
||||
|
||||
/*
|
||||
* Lock SPI bus for exclusive usage until FPGA programming is done.
|
||||
* SPI bus will be released in sysconfig_spi_bitstream_burst_complete().
|
||||
*/
|
||||
spi_bus_lock(spi->controller);
|
||||
|
||||
ret = spi_sync_locked(spi, &msg);
|
||||
if (ret)
|
||||
spi_bus_unlock(spi->controller);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sysconfig_spi_bitstream_burst_write(struct sysconfig_priv *priv,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(priv->dev);
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = buf,
|
||||
.len = len,
|
||||
.cs_change = 1,
|
||||
};
|
||||
struct spi_message msg;
|
||||
|
||||
spi_message_init_with_transfers(&msg, &xfer, 1);
|
||||
|
||||
return spi_sync_locked(spi, &msg);
|
||||
}
|
||||
|
||||
static int sysconfig_spi_bitstream_burst_complete(struct sysconfig_priv *priv)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(priv->dev);
|
||||
|
||||
/* Bitstream burst write is done, release SPI bus */
|
||||
spi_bus_unlock(spi->controller);
|
||||
|
||||
/* Toggle CS to finish bitstream write */
|
||||
return spi_write(spi, NULL, 0);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
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;
|
||||
}
|
||||
|
||||
if (!spi_max_speed)
|
||||
return -EINVAL;
|
||||
|
||||
if (spi->max_speed_hz > *spi_max_speed) {
|
||||
dev_err(dev, "SPI speed %u is too high, maximum speed is %u\n",
|
||||
spi->max_speed_hz, *spi_max_speed);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->dev = dev;
|
||||
priv->command_transfer = sysconfig_spi_cmd_transfer;
|
||||
priv->bitstream_burst_write_init = sysconfig_spi_bitstream_burst_init;
|
||||
priv->bitstream_burst_write = sysconfig_spi_bitstream_burst_write;
|
||||
priv->bitstream_burst_write_complete = sysconfig_spi_bitstream_burst_complete;
|
||||
|
||||
return sysconfig_probe(priv);
|
||||
}
|
||||
|
||||
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),
|
||||
},
|
||||
};
|
||||
module_spi_driver(lattice_sysconfig_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Lattice sysCONFIG Slave SPI FPGA Manager");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,397 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Lattice FPGA sysCONFIG interface functions independent of port type.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fpga/fpga-mgr.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "lattice-sysconfig.h"
|
||||
|
||||
static int sysconfig_cmd_write(struct sysconfig_priv *priv, const void *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
return priv->command_transfer(priv, buf, buf_len, NULL, 0);
|
||||
}
|
||||
|
||||
static int sysconfig_cmd_read(struct sysconfig_priv *priv, const void *tx_buf,
|
||||
size_t tx_len, void *rx_buf, size_t rx_len)
|
||||
{
|
||||
return priv->command_transfer(priv, tx_buf, tx_len, rx_buf, rx_len);
|
||||
}
|
||||
|
||||
static int sysconfig_read_busy(struct sysconfig_priv *priv)
|
||||
{
|
||||
const u8 lsc_check_busy[] = SYSCONFIG_LSC_CHECK_BUSY;
|
||||
u8 busy;
|
||||
int ret;
|
||||
|
||||
ret = sysconfig_cmd_read(priv, lsc_check_busy, sizeof(lsc_check_busy),
|
||||
&busy, sizeof(busy));
|
||||
|
||||
return ret ? : busy;
|
||||
}
|
||||
|
||||
static int sysconfig_poll_busy(struct sysconfig_priv *priv)
|
||||
{
|
||||
int ret, busy;
|
||||
|
||||
ret = read_poll_timeout(sysconfig_read_busy, busy, busy <= 0,
|
||||
SYSCONFIG_POLL_INTERVAL_US,
|
||||
SYSCONFIG_POLL_BUSY_TIMEOUT_US, false, priv);
|
||||
|
||||
return ret ? : busy;
|
||||
}
|
||||
|
||||
static int sysconfig_read_status(struct sysconfig_priv *priv, u32 *status)
|
||||
{
|
||||
const u8 lsc_read_status[] = SYSCONFIG_LSC_READ_STATUS;
|
||||
__be32 device_status;
|
||||
int ret;
|
||||
|
||||
ret = sysconfig_cmd_read(priv, lsc_read_status, sizeof(lsc_read_status),
|
||||
&device_status, sizeof(device_status));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*status = be32_to_cpu(device_status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysconfig_poll_status(struct sysconfig_priv *priv, u32 *status)
|
||||
{
|
||||
int ret = sysconfig_poll_busy(priv);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sysconfig_read_status(priv, status);
|
||||
}
|
||||
|
||||
static int sysconfig_poll_gpio(struct gpio_desc *gpio, bool is_active)
|
||||
{
|
||||
int ret, val;
|
||||
|
||||
ret = read_poll_timeout(gpiod_get_value, val,
|
||||
val < 0 || !!val == is_active,
|
||||
SYSCONFIG_POLL_INTERVAL_US,
|
||||
SYSCONFIG_POLL_GPIO_TIMEOUT_US, false, gpio);
|
||||
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sysconfig_gpio_refresh(struct sysconfig_priv *priv)
|
||||
{
|
||||
struct gpio_desc *program = priv->program;
|
||||
struct gpio_desc *init = priv->init;
|
||||
struct gpio_desc *done = priv->done;
|
||||
int ret;
|
||||
|
||||
/* Enter init mode */
|
||||
gpiod_set_value(program, 1);
|
||||
|
||||
ret = sysconfig_poll_gpio(init, true);
|
||||
if (!ret)
|
||||
ret = sysconfig_poll_gpio(done, false);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enter program mode */
|
||||
gpiod_set_value(program, 0);
|
||||
|
||||
return sysconfig_poll_gpio(init, false);
|
||||
}
|
||||
|
||||
static int sysconfig_lsc_refresh(struct sysconfig_priv *priv)
|
||||
{
|
||||
static const u8 lsc_refresh[] = SYSCONFIG_LSC_REFRESH;
|
||||
int ret;
|
||||
|
||||
ret = sysconfig_cmd_write(priv, lsc_refresh, sizeof(lsc_refresh));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(4000, 8000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysconfig_refresh(struct sysconfig_priv *priv)
|
||||
{
|
||||
struct gpio_desc *program = priv->program;
|
||||
struct gpio_desc *init = priv->init;
|
||||
struct gpio_desc *done = priv->done;
|
||||
|
||||
if (program && init && done)
|
||||
return sysconfig_gpio_refresh(priv);
|
||||
|
||||
return sysconfig_lsc_refresh(priv);
|
||||
}
|
||||
|
||||
static int sysconfig_isc_enable(struct sysconfig_priv *priv)
|
||||
{
|
||||
u8 isc_enable[] = SYSCONFIG_ISC_ENABLE;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = sysconfig_cmd_write(priv, isc_enable, sizeof(isc_enable));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sysconfig_poll_status(priv, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (status & SYSCONFIG_STATUS_FAIL)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysconfig_isc_erase(struct sysconfig_priv *priv)
|
||||
{
|
||||
u8 isc_erase[] = SYSCONFIG_ISC_ERASE;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
ret = sysconfig_cmd_write(priv, isc_erase, sizeof(isc_erase));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sysconfig_poll_status(priv, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (status & SYSCONFIG_STATUS_FAIL)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysconfig_isc_init(struct sysconfig_priv *priv)
|
||||
{
|
||||
int ret = sysconfig_isc_enable(priv);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sysconfig_isc_erase(priv);
|
||||
}
|
||||
|
||||
static int sysconfig_lsc_init_addr(struct sysconfig_priv *priv)
|
||||
{
|
||||
const u8 lsc_init_addr[] = SYSCONFIG_LSC_INIT_ADDR;
|
||||
|
||||
return sysconfig_cmd_write(priv, lsc_init_addr, sizeof(lsc_init_addr));
|
||||
}
|
||||
|
||||
static int sysconfig_burst_write_init(struct sysconfig_priv *priv)
|
||||
{
|
||||
return priv->bitstream_burst_write_init(priv);
|
||||
}
|
||||
|
||||
static int sysconfig_burst_write_complete(struct sysconfig_priv *priv)
|
||||
{
|
||||
return priv->bitstream_burst_write_complete(priv);
|
||||
}
|
||||
|
||||
static int sysconfig_bitstream_burst_write(struct sysconfig_priv *priv,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret = priv->bitstream_burst_write(priv, buf, count);
|
||||
|
||||
if (ret)
|
||||
sysconfig_burst_write_complete(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sysconfig_isc_disable(struct sysconfig_priv *priv)
|
||||
{
|
||||
const u8 isc_disable[] = SYSCONFIG_ISC_DISABLE;
|
||||
|
||||
return sysconfig_cmd_write(priv, isc_disable, sizeof(isc_disable));
|
||||
}
|
||||
|
||||
static void sysconfig_cleanup(struct sysconfig_priv *priv)
|
||||
{
|
||||
sysconfig_isc_erase(priv);
|
||||
sysconfig_refresh(priv);
|
||||
}
|
||||
|
||||
static int sysconfig_isc_finish(struct sysconfig_priv *priv)
|
||||
{
|
||||
struct gpio_desc *done_gpio = priv->done;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
if (done_gpio) {
|
||||
ret = sysconfig_isc_disable(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sysconfig_poll_gpio(done_gpio, true);
|
||||
}
|
||||
|
||||
ret = sysconfig_poll_status(priv, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((status & SYSCONFIG_STATUS_DONE) &&
|
||||
!(status & SYSCONFIG_STATUS_BUSY) &&
|
||||
!(status & SYSCONFIG_STATUS_ERR))
|
||||
return sysconfig_isc_disable(priv);
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
static enum fpga_mgr_states sysconfig_ops_state(struct fpga_manager *mgr)
|
||||
{
|
||||
struct sysconfig_priv *priv = mgr->priv;
|
||||
struct gpio_desc *done = priv->done;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
if (done && (gpiod_get_value(done) > 0))
|
||||
return FPGA_MGR_STATE_OPERATING;
|
||||
|
||||
ret = sysconfig_read_status(priv, &status);
|
||||
if (!ret && (status & SYSCONFIG_STATUS_DONE))
|
||||
return FPGA_MGR_STATE_OPERATING;
|
||||
|
||||
return FPGA_MGR_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
static int sysconfig_ops_write_init(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct sysconfig_priv *priv = mgr->priv;
|
||||
struct device *dev = &mgr->dev;
|
||||
int ret;
|
||||
|
||||
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
|
||||
dev_err(dev, "Partial reconfiguration is not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Enter program mode */
|
||||
ret = sysconfig_refresh(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to go to program mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enter ISC mode */
|
||||
ret = sysconfig_isc_init(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to go to ISC mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize the Address Shift Register */
|
||||
ret = sysconfig_lsc_init_addr(priv);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to initialize the Address Shift Register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Prepare for bitstream burst write */
|
||||
ret = sysconfig_burst_write_init(priv);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to prepare for bitstream burst write\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sysconfig_ops_write(struct fpga_manager *mgr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
return sysconfig_bitstream_burst_write(mgr->priv, buf, count);
|
||||
}
|
||||
|
||||
static int sysconfig_ops_write_complete(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info)
|
||||
{
|
||||
struct sysconfig_priv *priv = mgr->priv;
|
||||
struct device *dev = &mgr->dev;
|
||||
int ret;
|
||||
|
||||
ret = sysconfig_burst_write_complete(priv);
|
||||
if (!ret)
|
||||
ret = sysconfig_poll_busy(priv);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "Error while waiting bitstream write to finish\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = sysconfig_isc_finish(priv);
|
||||
|
||||
fail:
|
||||
if (ret)
|
||||
sysconfig_cleanup(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct fpga_manager_ops sysconfig_fpga_mgr_ops = {
|
||||
.state = sysconfig_ops_state,
|
||||
.write_init = sysconfig_ops_write_init,
|
||||
.write = sysconfig_ops_write,
|
||||
.write_complete = sysconfig_ops_write_complete,
|
||||
};
|
||||
|
||||
int sysconfig_probe(struct sysconfig_priv *priv)
|
||||
{
|
||||
struct gpio_desc *program, *init, *done;
|
||||
struct device *dev = priv->dev;
|
||||
struct fpga_manager *mgr;
|
||||
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (!priv->command_transfer ||
|
||||
!priv->bitstream_burst_write_init ||
|
||||
!priv->bitstream_burst_write ||
|
||||
!priv->bitstream_burst_write_complete) {
|
||||
dev_err(dev, "Essential callback is missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
program = devm_gpiod_get_optional(dev, "program", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(program))
|
||||
return dev_err_probe(dev, PTR_ERR(program),
|
||||
"Failed to get PROGRAM GPIO\n");
|
||||
|
||||
init = devm_gpiod_get_optional(dev, "init", GPIOD_IN);
|
||||
if (IS_ERR(init))
|
||||
return dev_err_probe(dev, PTR_ERR(init),
|
||||
"Failed to get INIT GPIO\n");
|
||||
|
||||
done = devm_gpiod_get_optional(dev, "done", GPIOD_IN);
|
||||
if (IS_ERR(done))
|
||||
return dev_err_probe(dev, PTR_ERR(done),
|
||||
"Failed to get DONE GPIO\n");
|
||||
|
||||
priv->program = program;
|
||||
priv->init = init;
|
||||
priv->done = done;
|
||||
|
||||
mgr = devm_fpga_mgr_register(dev, "Lattice sysCONFIG FPGA Manager",
|
||||
&sysconfig_fpga_mgr_ops, priv);
|
||||
|
||||
return PTR_ERR_OR_ZERO(mgr);
|
||||
}
|
||||
EXPORT_SYMBOL(sysconfig_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Lattice sysCONFIG FPGA Manager Core");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,39 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __LATTICE_SYSCONFIG_H
|
||||
#define __LATTICE_SYSCONFIG_H
|
||||
|
||||
#define SYSCONFIG_ISC_ENABLE {0xC6, 0x00, 0x00, 0x00}
|
||||
#define SYSCONFIG_ISC_DISABLE {0x26, 0x00, 0x00, 0x00}
|
||||
#define SYSCONFIG_ISC_ERASE {0x0E, 0x01, 0x00, 0x00}
|
||||
#define SYSCONFIG_LSC_READ_STATUS {0x3C, 0x00, 0x00, 0x00}
|
||||
#define SYSCONFIG_LSC_CHECK_BUSY {0xF0, 0x00, 0x00, 0x00}
|
||||
#define SYSCONFIG_LSC_REFRESH {0x79, 0x00, 0x00, 0x00}
|
||||
#define SYSCONFIG_LSC_INIT_ADDR {0x46, 0x00, 0x00, 0x00}
|
||||
#define SYSCONFIG_LSC_BITSTREAM_BURST {0x7a, 0x00, 0x00, 0x00}
|
||||
|
||||
#define SYSCONFIG_STATUS_DONE BIT(8)
|
||||
#define SYSCONFIG_STATUS_BUSY BIT(12)
|
||||
#define SYSCONFIG_STATUS_FAIL BIT(13)
|
||||
#define SYSCONFIG_STATUS_ERR GENMASK(25, 23)
|
||||
|
||||
#define SYSCONFIG_POLL_INTERVAL_US 30
|
||||
#define SYSCONFIG_POLL_BUSY_TIMEOUT_US 1000000
|
||||
#define SYSCONFIG_POLL_GPIO_TIMEOUT_US 100000
|
||||
|
||||
struct sysconfig_priv {
|
||||
struct gpio_desc *program;
|
||||
struct gpio_desc *init;
|
||||
struct gpio_desc *done;
|
||||
struct device *dev;
|
||||
int (*command_transfer)(struct sysconfig_priv *priv, const void *tx_buf,
|
||||
size_t tx_len, void *rx_buf, size_t rx_len);
|
||||
int (*bitstream_burst_write_init)(struct sysconfig_priv *priv);
|
||||
int (*bitstream_burst_write)(struct sysconfig_priv *priv,
|
||||
const char *tx_buf, size_t tx_len);
|
||||
int (*bitstream_burst_write_complete)(struct sysconfig_priv *priv);
|
||||
};
|
||||
|
||||
int sysconfig_probe(struct sysconfig_priv *priv);
|
||||
|
||||
#endif /* __LATTICE_SYSCONFIG_H */
|
|
@ -582,11 +582,9 @@ static int zynq_fpga_probe(struct platform_device *pdev)
|
|||
return priv->irq;
|
||||
|
||||
priv->clk = devm_clk_get(dev, "ref_clk");
|
||||
if (IS_ERR(priv->clk)) {
|
||||
if (PTR_ERR(priv->clk) != -EPROBE_DEFER)
|
||||
dev_err(dev, "input clock not found\n");
|
||||
return PTR_ERR(priv->clk);
|
||||
}
|
||||
if (IS_ERR(priv->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->clk),
|
||||
"input clock not found\n");
|
||||
|
||||
err = clk_prepare_enable(priv->clk);
|
||||
if (err) {
|
||||
|
|
Loading…
Reference in New Issue