* [PATCH 0/5] ARM: zynqmp: add support for bitstream loading @ 2019-10-24 10:26 Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 1/5] ARM: zynqmp: dts: move firmware node to src tree Thomas Hämmerle ` (5 more replies) 0 siblings, 6 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-24 10:26 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> Michael Tretter (2): ARM: zynqmp: dts: move firmware node to src tree firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Haemmerle (3): firmware-zynqmp: extend driver with fpga relavant functions firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA ARM: zynqmp: dts: move pcap node to src tree arch/arm/configs/zynqmp_defconfig | 1 + arch/arm/dts/zynqmp-zcu104-revA.dts | 1 - arch/arm/dts/zynqmp.dtsi | 17 - arch/arm/mach-zynqmp/firmware-zynqmp.c | 47 +++ .../arm/mach-zynqmp/include/mach/firmware-zynqmp.h | 10 + drivers/firmware/Kconfig | 5 + drivers/firmware/Makefile | 1 + drivers/firmware/zynqmp-fpga.c | 366 +++++++++++++++++++++ dts/src/arm64/xilinx/zynqmp.dtsi | 10 + 9 files changed, 440 insertions(+), 18 deletions(-) delete mode 100644 arch/arm/dts/zynqmp.dtsi create mode 100644 drivers/firmware/zynqmp-fpga.c -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/5] ARM: zynqmp: dts: move firmware node to src tree 2019-10-24 10:26 [PATCH 0/5] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle @ 2019-10-24 10:26 ` Thomas Hämmerle 2019-10-24 12:56 ` Michael Tretter 2019-10-24 10:26 ` [PATCH 4/5] ARM: zynqmp: dts: move pcap " Thomas Hämmerle ` (4 subsequent siblings) 5 siblings, 1 reply; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-24 10:26 UTC (permalink / raw) To: barebox; +Cc: Michael Tretter From: Michael Tretter <m.tretter@pengutronix.de> The firmware node will be added to the mainline device tree. As it will eventually enter Barebox via a device tree sync, add it to the src tree already. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de> --- arch/arm/dts/zynqmp-zcu104-revA.dts | 1 - arch/arm/dts/zynqmp.dtsi | 17 ----------------- dts/src/arm64/xilinx/zynqmp.dtsi | 7 +++++++ 3 files changed, 7 insertions(+), 18 deletions(-) delete mode 100644 arch/arm/dts/zynqmp.dtsi diff --git a/arch/arm/dts/zynqmp-zcu104-revA.dts b/arch/arm/dts/zynqmp-zcu104-revA.dts index c03112d..8b8dd84 100644 --- a/arch/arm/dts/zynqmp-zcu104-revA.dts +++ b/arch/arm/dts/zynqmp-zcu104-revA.dts @@ -8,5 +8,4 @@ */ #include <arm64/xilinx/zynqmp-zcu104-revA.dts> -#include "zynqmp.dtsi" #include "zynqmp-clk.dtsi" diff --git a/arch/arm/dts/zynqmp.dtsi b/arch/arm/dts/zynqmp.dtsi deleted file mode 100644 index 59984ee..0000000 --- a/arch/arm/dts/zynqmp.dtsi +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * dts file for Xilinx ZynqMP - * - * (C) Copyright 2014 - 2015, Xilinx, Inc. - * - * Michal Simek <michal.simek@xilinx.com> - */ - -/ { - firmware { - zynqmp_firmware: zynqmp-firmware { - compatible = "xlnx,zynqmp-firmware"; - method = "smc"; - }; - }; -}; diff --git a/dts/src/arm64/xilinx/zynqmp.dtsi b/dts/src/arm64/xilinx/zynqmp.dtsi index 9aa6734..9115eae 100644 --- a/dts/src/arm64/xilinx/zynqmp.dtsi +++ b/dts/src/arm64/xilinx/zynqmp.dtsi @@ -115,6 +115,13 @@ method = "smc"; }; + firmware { + zynqmp_firmware: zynqmp-firmware { + compatible = "xlnx,zynqmp-firmware"; + method = "smc"; + }; + }; + timer { compatible = "arm,armv8-timer"; interrupt-parent = <&gic>; -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/5] ARM: zynqmp: dts: move firmware node to src tree 2019-10-24 10:26 ` [PATCH 1/5] ARM: zynqmp: dts: move firmware node to src tree Thomas Hämmerle @ 2019-10-24 12:56 ` Michael Tretter 0 siblings, 0 replies; 15+ messages in thread From: Michael Tretter @ 2019-10-24 12:56 UTC (permalink / raw) To: Thomas Hämmerle; +Cc: barebox On Thu, 24 Oct 2019 10:26:50 +0000, Thomas Hämmerle wrote: > From: Michael Tretter <m.tretter@pengutronix.de> > > The firmware node will be added to the mainline device tree. As it will > eventually enter Barebox via a device tree sync, add it to the src tree > already. Not sure, but I think that we should wait with the device tree changes until they arrive via a sync with the kernel tree. Michael > > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de> > --- > arch/arm/dts/zynqmp-zcu104-revA.dts | 1 - > arch/arm/dts/zynqmp.dtsi | 17 ----------------- > dts/src/arm64/xilinx/zynqmp.dtsi | 7 +++++++ > 3 files changed, 7 insertions(+), 18 deletions(-) > delete mode 100644 arch/arm/dts/zynqmp.dtsi > > diff --git a/arch/arm/dts/zynqmp-zcu104-revA.dts b/arch/arm/dts/zynqmp-zcu104-revA.dts > index c03112d..8b8dd84 100644 > --- a/arch/arm/dts/zynqmp-zcu104-revA.dts > +++ b/arch/arm/dts/zynqmp-zcu104-revA.dts > @@ -8,5 +8,4 @@ > */ > > #include <arm64/xilinx/zynqmp-zcu104-revA.dts> > -#include "zynqmp.dtsi" > #include "zynqmp-clk.dtsi" > diff --git a/arch/arm/dts/zynqmp.dtsi b/arch/arm/dts/zynqmp.dtsi > deleted file mode 100644 > index 59984ee..0000000 > --- a/arch/arm/dts/zynqmp.dtsi > +++ /dev/null > @@ -1,17 +0,0 @@ > -// SPDX-License-Identifier: GPL-2.0+ > -/* > - * dts file for Xilinx ZynqMP > - * > - * (C) Copyright 2014 - 2015, Xilinx, Inc. > - * > - * Michal Simek <michal.simek@xilinx.com> > - */ > - > -/ { > - firmware { > - zynqmp_firmware: zynqmp-firmware { > - compatible = "xlnx,zynqmp-firmware"; > - method = "smc"; > - }; > - }; > -}; > diff --git a/dts/src/arm64/xilinx/zynqmp.dtsi b/dts/src/arm64/xilinx/zynqmp.dtsi > index 9aa6734..9115eae 100644 > --- a/dts/src/arm64/xilinx/zynqmp.dtsi > +++ b/dts/src/arm64/xilinx/zynqmp.dtsi > @@ -115,6 +115,13 @@ > method = "smc"; > }; > > + firmware { > + zynqmp_firmware: zynqmp-firmware { > + compatible = "xlnx,zynqmp-firmware"; > + method = "smc"; > + }; > + }; > + > timer { > compatible = "arm,armv8-timer"; > interrupt-parent = <&gic>; _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 4/5] ARM: zynqmp: dts: move pcap node to src tree 2019-10-24 10:26 [PATCH 0/5] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 1/5] ARM: zynqmp: dts: move firmware node to src tree Thomas Hämmerle @ 2019-10-24 10:26 ` Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 3/5] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Thomas Hämmerle ` (3 subsequent siblings) 5 siblings, 0 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-24 10:26 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> The pcap node will be added to the mainline device tree. As it will eventually enter Barebox via a device tree sync, add it to the src tree already. Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> --- dts/src/arm64/xilinx/zynqmp.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dts/src/arm64/xilinx/zynqmp.dtsi b/dts/src/arm64/xilinx/zynqmp.dtsi index 9115eae..1d1e531 100644 --- a/dts/src/arm64/xilinx/zynqmp.dtsi +++ b/dts/src/arm64/xilinx/zynqmp.dtsi @@ -119,6 +119,9 @@ zynqmp_firmware: zynqmp-firmware { compatible = "xlnx,zynqmp-firmware"; method = "smc"; + zynqmp_pcap: pcap { + compatible = "xlnx,zynqmp-pcap-fpga"; + }; }; }; -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/5] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA 2019-10-24 10:26 [PATCH 0/5] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 1/5] ARM: zynqmp: dts: move firmware node to src tree Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 4/5] ARM: zynqmp: dts: move pcap " Thomas Hämmerle @ 2019-10-24 10:26 ` Thomas Hämmerle 2019-10-24 12:56 ` Michael Tretter 2019-10-24 10:26 ` [PATCH 5/5] firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Hämmerle ` (2 subsequent siblings) 5 siblings, 1 reply; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-24 10:26 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> The driver provides functionalities to check and load a bitstream to FPGA. A boolean parameter to check if FPGA is already programmed is added. Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> --- arch/arm/configs/zynqmp_defconfig | 1 + .../arm/mach-zynqmp/include/mach/firmware-zynqmp.h | 2 + drivers/firmware/Kconfig | 5 + drivers/firmware/Makefile | 1 + drivers/firmware/zynqmp-fpga.c | 315 +++++++++++++++++++++ 5 files changed, 324 insertions(+) create mode 100644 drivers/firmware/zynqmp-fpga.c diff --git a/arch/arm/configs/zynqmp_defconfig b/arch/arm/configs/zynqmp_defconfig index 4dea964..834212e 100644 --- a/arch/arm/configs/zynqmp_defconfig +++ b/arch/arm/configs/zynqmp_defconfig @@ -35,4 +35,5 @@ CONFIG_CMD_OFTREE=y CONFIG_CMD_TIME=y CONFIG_DRIVER_SERIAL_CADENCE=y # CONFIG_SPI is not set +CONFIG_FIRMWARE_ZYNQMP_PL=y CONFIG_DIGEST=y diff --git a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h index f19d73d..6fcfba5 100644 --- a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h +++ b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h @@ -23,6 +23,8 @@ #define ZYNQMP_FPGA_BIT_ENC_DEV_KEY BIT(4) #define ZYNQMP_FPGA_BIT_ONLY_BIN BIT(5) +#define ZYNQMP_PCAP_STATUS_FPGA_DONE BIT(3) + enum pm_ioctl_id { IOCTL_SET_PLL_FRAC_MODE = 8, IOCTL_GET_PLL_FRAC_MODE, diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 710b500..3113f40 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -14,4 +14,9 @@ config FIRMWARE_ALTERA_SOCFPGA bool "Altera SoCFPGA fpga loader" depends on ARCH_SOCFPGA select FIRMWARE + +config FIRMWARE_ZYNQMP_FPGA + bool "Xilinx ZynqMP FPGA loader" + depends on ARCH_ZYNQMP + select FIRMWARE endmenu diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index c3a3c34..b162b08 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o +obj-$(CONFIG_FIRMWARE_ZYNQMP_FPGA) += zynqmp-fpga.o diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c new file mode 100644 index 0000000..6a32d28 --- /dev/null +++ b/drivers/firmware/zynqmp-fpga.c @@ -0,0 +1,315 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Xilinx Zynq MPSoC PL loading + * + * Copyright (c) 2018 Thomas Haemmerle <thomas.haemmerle@wolfvision.net> + * + * based on U-Boot zynqmppl code + * + * (C) Copyright 2015 - 2016, Xilinx, Inc, + * Michal Simek <michal.simek@xilinx.com> + * Siva Durga Prasad <siva.durga.paladugu@xilinx.com> * + */ + +#include <firmware.h> +#include <common.h> +#include <init.h> +#include <dma.h> +#include <mach/firmware-zynqmp.h> + +#define ZYNQMP_PM_VERSION_1_1 ((1 << 16) | 1) + +#define ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL BIT(0) + +#define ZYNQMP_PM_VERSION_1_0_FEATURES 0 +#define ZYNQMP_PM_VERSION_1_1_FEATURES ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL + +#define DUMMY_WORD 0xFFFFFFFF + +#define XILINX_BYTE_ORDER_BIT 0 +#define XILINX_BYTE_ORDER_BIN 1 + +struct fpgamgr { + struct firmware_handler fh; + struct device_d dev; + const struct zynqmp_eemi_ops *eemi_ops; + int programmed; + char *buf; + size_t size; + u32 features; +}; + +/* Xilinx binary format header */ +static const u32 bin_format[] = { + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + 0x000000bb, + 0x11220044, + DUMMY_WORD, + DUMMY_WORD, + 0xaa995566, +}; + +static void copy_words_swapped(u32 *dst, const u32 *src, size_t size) +{ + int i; + + for (i = 0; i < size; i++) + dst[i] = __swab32(src[i]); +} + +static int get_byte_order(const u32 *buf, size_t size) +{ + u32 buf_check[ARRAY_SIZE(bin_format)]; + + memcpy(buf_check, buf, ARRAY_SIZE(buf_check)); + if (memcmp(buf_check, bin_format, sizeof(buf_check)) == 0) + return XILINX_BYTE_ORDER_BIT; + + copy_words_swapped(buf_check, buf, ARRAY_SIZE(buf_check)); + if (memcmp(buf_check, bin_format, sizeof(buf_check)) == 0) + return XILINX_BYTE_ORDER_BIN; + + return -EINVAL; +} + +static int get_header_length(const char *buf, size_t size) +{ + u32 *buf_u32; + int p; + + for (p = 0; p < size; p++) { + buf_u32 = (u32 *)&buf[p]; + if (*buf_u32 == DUMMY_WORD) + return p; + } + return -EINVAL; +} + +static int fpgamgr_program_finish(struct firmware_handler *fh) +{ + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + char *buf_aligned; + u32 *buf_size = NULL; + int header_length = 0, byte_order = 0; + u64 addr; + int status = 0, xilfpga_old = 1; + u8 flags = 0; + + if (!mgr->buf) { + status = -ENOBUFS; + dev_err(&mgr->dev, "buffer is NULL\n"); + goto err_free; + } + + header_length = get_header_length(mgr->buf, mgr->size); + if (header_length < 0) { + status = header_length; + goto err_free; + } + + byte_order = get_byte_order((u32 *)&mgr->buf[header_length], + mgr->size - header_length); + if (byte_order < 0) { + status = byte_order; + goto err_free; + } + + if (mgr->features & ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL) { + /* PMUFW version > 1.0 accepts both byte orders*/ + byte_order = 0; + xilfpga_old = 0; + } else { + buf_size = dma_alloc_coherent(sizeof(*buf_size), + DMA_ADDRESS_BROKEN); + if (!buf_size) { + status = -ENOBUFS; + goto err_free; + } + *buf_size = mgr->size - header_length; + } + + buf_aligned = dma_alloc_coherent(mgr->size - header_length, + DMA_ADDRESS_BROKEN); + if (!buf_aligned) { + status = -ENOBUFS; + goto err_free; + } + + if (byte_order == XILINX_BYTE_ORDER_BIN) + copy_words_swapped((u32 *)buf_aligned, + (u32 *)(&(mgr->buf[header_length])), + (mgr->size - header_length) / sizeof(u32)); + else + memcpy((u32 *)buf_aligned, (u32 *)(&(mgr->buf[header_length])), + (mgr->size - header_length)); + + addr = (u64)buf_aligned; + + /* we do not provide a header */ + flags |= ZYNQMP_FPGA_BIT_ONLY_BIN; + + if (xilfpga_old && buf_size) { + status = mgr->eemi_ops->fpga_load(addr, + (u32)(uintptr_t)buf_size, + flags); + dma_free_coherent(buf_size, 0, sizeof(*buf_size)); + } else { + status = mgr->eemi_ops->fpga_load(addr, + (u32)(mgr->size - header_length), + flags); + } + + if (status < 0) + dev_err(&mgr->dev, "unable to load fpga\n"); + + dma_free_coherent(buf_aligned, 0, mgr->size - header_length); + + err_free: + free(mgr->buf); + + return status; +} + +static int fpgamgr_program_write_buf(struct firmware_handler *fh, + const void *buf, size_t size) +{ + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + + /* Since write() is called by copy_file, we only receive chuncks with + * size RW_BUF_SIZE of the bitstream. + * Buffer the chunks here and handle it in close() + */ + + mgr->buf = realloc(mgr->buf, mgr->size + size); + if (!mgr->buf) + return -ENOBUFS; + + memcpy(&(mgr->buf[mgr->size]), buf, size); + mgr->size += size; + + return 0; +} + +static int fpgamgr_program_start(struct firmware_handler *fh) +{ + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + + mgr->size = 0; + mgr->buf = NULL; + + return 0; +} + +static int programmed_get(struct param_d *p, void *priv) +{ + struct fpgamgr *mgr = priv; + u32 status = 0x00; + int ret = 0; + + ret = mgr->eemi_ops->fpga_getstatus(&status); + if (ret) + return ret; + + mgr->programmed = !!(status & ZYNQMP_PCAP_STATUS_FPGA_DONE); + + return 0; +} + +static int zynqmp_fpga_probe(struct device_d *dev) +{ + struct fpgamgr *mgr; + struct firmware_handler *fh; + const char *alias = of_alias_get(dev->device_node); + const char *model = NULL; + struct param_d *p; + u32 api_version; + int ret; + + mgr = xzalloc(sizeof(*mgr)); + fh = &mgr->fh; + + if (alias) + fh->id = xstrdup(alias); + else + fh->id = xstrdup("zynqmp-fpga-manager"); + + fh->open = fpgamgr_program_start; + fh->write = fpgamgr_program_write_buf; + fh->close = fpgamgr_program_finish; + of_property_read_string(dev->device_node, "compatible", &model); + if (model) + fh->model = xstrdup(model); + fh->dev = dev; + + mgr->eemi_ops = zynqmp_pm_get_eemi_ops(); + + ret = mgr->eemi_ops->get_api_version(&api_version); + if (ret) { + dev_err(&mgr->dev, "could not get API version\n"); + goto out; + } + + mgr->features = 0; + + if (api_version >= ZYNQMP_PM_VERSION_1_1) + mgr->features |= ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL; + + dev_dbg(dev, "Registering ZynqMP FPGA programmer\n"); + mgr->dev.id = DEVICE_ID_SINGLE; + dev_set_name(&mgr->dev, "zynqmp_fpga"); + mgr->dev.parent = dev; + ret = register_device(&mgr->dev); + if (ret) + goto out; + + p = dev_add_param_bool(&mgr->dev, "programmed", NULL, programmed_get, + &mgr->programmed, mgr); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + goto out_unreg; + } + + fh->dev = &mgr->dev; + ret = firmwaremgr_register(fh); + if (ret != 0) { + free(mgr); + goto out_unreg; + } + + return 0; +out_unreg: + unregister_device(&mgr->dev); +out: + free(fh->id); + free(mgr); + + return ret; +} + +static struct of_device_id zynqmpp_fpga_id_table[] = { + { + .compatible = "xlnx,zynqmp-pcap-fpga", + }, +}; + +static struct driver_d zynqmp_fpga_driver = { + .name = "zynqmp_fpga_manager", + .of_compatible = DRV_OF_COMPAT(zynqmpp_fpga_id_table), + .probe = zynqmp_fpga_probe, +}; +device_platform_driver(zynqmp_fpga_driver); -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 3/5] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA 2019-10-24 10:26 ` [PATCH 3/5] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Thomas Hämmerle @ 2019-10-24 12:56 ` Michael Tretter 0 siblings, 0 replies; 15+ messages in thread From: Michael Tretter @ 2019-10-24 12:56 UTC (permalink / raw) To: Thomas Hämmerle; +Cc: barebox On Thu, 24 Oct 2019 10:26:51 +0000, Thomas Hämmerle wrote: > From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> > > The driver provides functionalities to check and load a bitstream to FPGA. > A boolean parameter to check if FPGA is already programmed is > added. > > Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> > --- > arch/arm/configs/zynqmp_defconfig | 1 + > .../arm/mach-zynqmp/include/mach/firmware-zynqmp.h | 2 + > drivers/firmware/Kconfig | 5 + > drivers/firmware/Makefile | 1 + > drivers/firmware/zynqmp-fpga.c | 315 +++++++++++++++++++++ > 5 files changed, 324 insertions(+) > create mode 100644 drivers/firmware/zynqmp-fpga.c > > diff --git a/arch/arm/configs/zynqmp_defconfig b/arch/arm/configs/zynqmp_defconfig > index 4dea964..834212e 100644 > --- a/arch/arm/configs/zynqmp_defconfig > +++ b/arch/arm/configs/zynqmp_defconfig > @@ -35,4 +35,5 @@ CONFIG_CMD_OFTREE=y > CONFIG_CMD_TIME=y > CONFIG_DRIVER_SERIAL_CADENCE=y > # CONFIG_SPI is not set > +CONFIG_FIRMWARE_ZYNQMP_PL=y > CONFIG_DIGEST=y > diff --git a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h > index f19d73d..6fcfba5 100644 > --- a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h > +++ b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h > @@ -23,6 +23,8 @@ > #define ZYNQMP_FPGA_BIT_ENC_DEV_KEY BIT(4) > #define ZYNQMP_FPGA_BIT_ONLY_BIN BIT(5) > > +#define ZYNQMP_PCAP_STATUS_FPGA_DONE BIT(3) > + Should be part of the previous patch. > enum pm_ioctl_id { > IOCTL_SET_PLL_FRAC_MODE = 8, > IOCTL_GET_PLL_FRAC_MODE, > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig > index 710b500..3113f40 100644 > --- a/drivers/firmware/Kconfig > +++ b/drivers/firmware/Kconfig > @@ -14,4 +14,9 @@ config FIRMWARE_ALTERA_SOCFPGA > bool "Altera SoCFPGA fpga loader" > depends on ARCH_SOCFPGA > select FIRMWARE > + > +config FIRMWARE_ZYNQMP_FPGA > + bool "Xilinx ZynqMP FPGA loader" > + depends on ARCH_ZYNQMP > + select FIRMWARE > endmenu > diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile > index c3a3c34..b162b08 100644 > --- a/drivers/firmware/Makefile > +++ b/drivers/firmware/Makefile > @@ -1,2 +1,3 @@ > obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o > obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o > +obj-$(CONFIG_FIRMWARE_ZYNQMP_FPGA) += zynqmp-fpga.o > diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c > new file mode 100644 > index 0000000..6a32d28 > --- /dev/null > +++ b/drivers/firmware/zynqmp-fpga.c > @@ -0,0 +1,315 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Xilinx Zynq MPSoC PL loading > + * > + * Copyright (c) 2018 Thomas Haemmerle <thomas.haemmerle@wolfvision.net> > + * > + * based on U-Boot zynqmppl code > + * > + * (C) Copyright 2015 - 2016, Xilinx, Inc, > + * Michal Simek <michal.simek@xilinx.com> > + * Siva Durga Prasad <siva.durga.paladugu@xilinx.com> * > + */ > + > +#include <firmware.h> > +#include <common.h> > +#include <init.h> > +#include <dma.h> > +#include <mach/firmware-zynqmp.h> > + > +#define ZYNQMP_PM_VERSION_1_1 ((1 << 16) | 1) A macro ZYNQMP_PM_VERSION(MAJOR, MINOR) would be more intuitive. > + > +#define ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL BIT(0) > + > +#define ZYNQMP_PM_VERSION_1_0_FEATURES 0 > +#define ZYNQMP_PM_VERSION_1_1_FEATURES ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL > + > +#define DUMMY_WORD 0xFFFFFFFF > + > +#define XILINX_BYTE_ORDER_BIT 0 > +#define XILINX_BYTE_ORDER_BIN 1 > + > +struct fpgamgr { > + struct firmware_handler fh; > + struct device_d dev; > + const struct zynqmp_eemi_ops *eemi_ops; > + int programmed; > + char *buf; > + size_t size; > + u32 features; > +}; > + > +/* Xilinx binary format header */ > +static const u32 bin_format[] = { > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + DUMMY_WORD, > + 0x000000bb, > + 0x11220044, > + DUMMY_WORD, > + DUMMY_WORD, > + 0xaa995566, > +}; > + > +static void copy_words_swapped(u32 *dst, const u32 *src, size_t size) > +{ > + int i; > + > + for (i = 0; i < size; i++) > + dst[i] = __swab32(src[i]); > +} > + > +static int get_byte_order(const u32 *buf, size_t size) > +{ > + u32 buf_check[ARRAY_SIZE(bin_format)]; > + > + memcpy(buf_check, buf, ARRAY_SIZE(buf_check)); > + if (memcmp(buf_check, bin_format, sizeof(buf_check)) == 0) > + return XILINX_BYTE_ORDER_BIT; > + > + copy_words_swapped(buf_check, buf, ARRAY_SIZE(buf_check)); > + if (memcmp(buf_check, bin_format, sizeof(buf_check)) == 0) > + return XILINX_BYTE_ORDER_BIN; > + > + return -EINVAL; > +} The function does not only find the byte order, but also checks if the header is valid. I would prefer, if we could have a function for getting the byte order (no copy necessary, just check for one of the values that allow to distinguish the byte order) and another function for checking if the header is valid. > + > +static int get_header_length(const char *buf, size_t size) > +{ > + u32 *buf_u32; > + int p; > + > + for (p = 0; p < size; p++) { > + buf_u32 = (u32 *)&buf[p]; > + if (*buf_u32 == DUMMY_WORD) > + return p; > + } > + return -EINVAL; > +} > + > +static int fpgamgr_program_finish(struct firmware_handler *fh) > +{ > + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); > + char *buf_aligned; > + u32 *buf_size = NULL; > + int header_length = 0, byte_order = 0; > + u64 addr; > + int status = 0, xilfpga_old = 1; > + u8 flags = 0; > + > + if (!mgr->buf) { > + status = -ENOBUFS; > + dev_err(&mgr->dev, "buffer is NULL\n"); > + goto err_free; > + } > + > + header_length = get_header_length(mgr->buf, mgr->size); > + if (header_length < 0) { > + status = header_length; > + goto err_free; > + } > + > + byte_order = get_byte_order((u32 *)&mgr->buf[header_length], > + mgr->size - header_length); I think helper variables u32 *body and size_t body_size would probably make this a lot more readable. > + if (byte_order < 0) { > + status = byte_order; > + goto err_free; > + } > + > + if (mgr->features & ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL) { > + /* PMUFW version > 1.0 accepts both byte orders*/ > + byte_order = 0; > + xilfpga_old = 0; > + } else { > + buf_size = dma_alloc_coherent(sizeof(*buf_size), > + DMA_ADDRESS_BROKEN); > + if (!buf_size) { > + status = -ENOBUFS; > + goto err_free; > + } > + *buf_size = mgr->size - header_length; > + } > + > + buf_aligned = dma_alloc_coherent(mgr->size - header_length, > + DMA_ADDRESS_BROKEN); > + if (!buf_aligned) { > + status = -ENOBUFS; > + goto err_free; > + } > + > + if (byte_order == XILINX_BYTE_ORDER_BIN) > + copy_words_swapped((u32 *)buf_aligned, > + (u32 *)(&(mgr->buf[header_length])), > + (mgr->size - header_length) / sizeof(u32)); > + else > + memcpy((u32 *)buf_aligned, (u32 *)(&(mgr->buf[header_length])), > + (mgr->size - header_length)); > + > + addr = (u64)buf_aligned; > + > + /* we do not provide a header */ > + flags |= ZYNQMP_FPGA_BIT_ONLY_BIN; > + > + if (xilfpga_old && buf_size) { The xilfpga_old variable is pretty hard to follow. Maybe always check for the feature flag or test the feature flag once and set a local variable (with a proper name) that is used whenever the feature flag is relevant. Michael > + status = mgr->eemi_ops->fpga_load(addr, > + (u32)(uintptr_t)buf_size, > + flags); > + dma_free_coherent(buf_size, 0, sizeof(*buf_size)); > + } else { > + status = mgr->eemi_ops->fpga_load(addr, > + (u32)(mgr->size - header_length), > + flags); > + } > + > + if (status < 0) > + dev_err(&mgr->dev, "unable to load fpga\n"); > + > + dma_free_coherent(buf_aligned, 0, mgr->size - header_length); > + > + err_free: > + free(mgr->buf); > + > + return status; > +} > + > +static int fpgamgr_program_write_buf(struct firmware_handler *fh, > + const void *buf, size_t size) > +{ > + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); > + > + /* Since write() is called by copy_file, we only receive chuncks with > + * size RW_BUF_SIZE of the bitstream. > + * Buffer the chunks here and handle it in close() > + */ > + > + mgr->buf = realloc(mgr->buf, mgr->size + size); > + if (!mgr->buf) > + return -ENOBUFS; > + > + memcpy(&(mgr->buf[mgr->size]), buf, size); > + mgr->size += size; > + > + return 0; > +} > + > +static int fpgamgr_program_start(struct firmware_handler *fh) > +{ > + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); > + > + mgr->size = 0; > + mgr->buf = NULL; > + > + return 0; > +} > + > +static int programmed_get(struct param_d *p, void *priv) > +{ > + struct fpgamgr *mgr = priv; > + u32 status = 0x00; > + int ret = 0; > + > + ret = mgr->eemi_ops->fpga_getstatus(&status); > + if (ret) > + return ret; > + > + mgr->programmed = !!(status & ZYNQMP_PCAP_STATUS_FPGA_DONE); > + > + return 0; > +} > + > +static int zynqmp_fpga_probe(struct device_d *dev) > +{ > + struct fpgamgr *mgr; > + struct firmware_handler *fh; > + const char *alias = of_alias_get(dev->device_node); > + const char *model = NULL; > + struct param_d *p; > + u32 api_version; > + int ret; > + > + mgr = xzalloc(sizeof(*mgr)); > + fh = &mgr->fh; > + > + if (alias) > + fh->id = xstrdup(alias); > + else > + fh->id = xstrdup("zynqmp-fpga-manager"); > + > + fh->open = fpgamgr_program_start; > + fh->write = fpgamgr_program_write_buf; > + fh->close = fpgamgr_program_finish; > + of_property_read_string(dev->device_node, "compatible", &model); > + if (model) > + fh->model = xstrdup(model); > + fh->dev = dev; > + > + mgr->eemi_ops = zynqmp_pm_get_eemi_ops(); > + > + ret = mgr->eemi_ops->get_api_version(&api_version); > + if (ret) { > + dev_err(&mgr->dev, "could not get API version\n"); > + goto out; > + } > + > + mgr->features = 0; > + > + if (api_version >= ZYNQMP_PM_VERSION_1_1) > + mgr->features |= ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL; > + > + dev_dbg(dev, "Registering ZynqMP FPGA programmer\n"); > + mgr->dev.id = DEVICE_ID_SINGLE; > + dev_set_name(&mgr->dev, "zynqmp_fpga"); > + mgr->dev.parent = dev; > + ret = register_device(&mgr->dev); > + if (ret) > + goto out; > + > + p = dev_add_param_bool(&mgr->dev, "programmed", NULL, programmed_get, > + &mgr->programmed, mgr); > + if (IS_ERR(p)) { > + ret = PTR_ERR(p); > + goto out_unreg; > + } > + > + fh->dev = &mgr->dev; > + ret = firmwaremgr_register(fh); > + if (ret != 0) { > + free(mgr); > + goto out_unreg; > + } > + > + return 0; > +out_unreg: > + unregister_device(&mgr->dev); > +out: > + free(fh->id); > + free(mgr); > + > + return ret; > +} > + > +static struct of_device_id zynqmpp_fpga_id_table[] = { > + { > + .compatible = "xlnx,zynqmp-pcap-fpga", > + }, > +}; > + > +static struct driver_d zynqmp_fpga_driver = { > + .name = "zynqmp_fpga_manager", > + .of_compatible = DRV_OF_COMPAT(zynqmpp_fpga_id_table), > + .probe = zynqmp_fpga_probe, > +}; > +device_platform_driver(zynqmp_fpga_driver); _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 5/5] firmware: zynqmp-fpga: print Xilinx bitstream header 2019-10-24 10:26 [PATCH 0/5] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle ` (2 preceding siblings ...) 2019-10-24 10:26 ` [PATCH 3/5] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Thomas Hämmerle @ 2019-10-24 10:26 ` Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 2/5] firmware-zynqmp: extend driver with fpga relavant functions Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 5 siblings, 0 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-24 10:26 UTC (permalink / raw) To: barebox; +Cc: Michael Tretter From: Michael Tretter <m.tretter@pengutronix.de> The bitstream header has 5 fields, that start with a char for the type. Four fields have a big-ending length and a null-terminated string for the design name, the part number, and the date and time of creation. The last field is a big-endian 32 bit unsigned int for the size of the bitstream. Print this info when loading the bitstream. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de> --- drivers/firmware/zynqmp-fpga.c | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c index 6a32d28..7bf880f 100644 --- a/drivers/firmware/zynqmp-fpga.c +++ b/drivers/firmware/zynqmp-fpga.c @@ -39,6 +39,19 @@ struct fpgamgr { u32 features; }; +struct bs_header { + __be16 length; + u8 padding[9]; + __be16 size; + char entries[0]; +} __attribute__ ((packed)); + +struct bs_header_entry { + char type; + __be16 length; + char data[0]; +} __attribute__ ((packed)); + /* Xilinx binary format header */ static const u32 bin_format[] = { DUMMY_WORD, @@ -100,6 +113,42 @@ static int get_header_length(const char *buf, size_t size) return -EINVAL; } +static void zynqmp_fpga_show_header(const struct device_d *dev, + struct bs_header *header, size_t size) +{ + struct bs_header_entry *entry; + unsigned int i; + unsigned int length; + + for (i = 0; i < size - sizeof(*header); i += sizeof(*entry) + length) { + entry = (struct bs_header_entry *)&header->entries[i]; + length = __be16_to_cpu(entry->length); + + switch (entry->type) { + case 'a': + printf("Design: %s\n", entry->data); + break; + case 'b': + printf("Part number: %s\n", entry->data); + break; + case 'c': + printf("Date: %s\n", entry->data); + break; + case 'd': + printf("Time: %s\n", entry->data); + break; + case 'e': + /* Size entry does not have a length but is be32 int */ + printf("Size: %d bytes\n", + (length << 16) + (entry->data[0] << 8) + entry->data[1]); + return; + default: + dev_warn(dev, "Invalid header entry: %c", entry->type); + return; + } + } +} + static int fpgamgr_program_finish(struct firmware_handler *fh) { struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); @@ -121,6 +170,8 @@ static int fpgamgr_program_finish(struct firmware_handler *fh) status = header_length; goto err_free; } + zynqmp_fpga_show_header(&mgr->dev, + (struct bs_header *)mgr->buf, header_length); byte_order = get_byte_order((u32 *)&mgr->buf[header_length], mgr->size - header_length); -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/5] firmware-zynqmp: extend driver with fpga relavant functions 2019-10-24 10:26 [PATCH 0/5] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle ` (3 preceding siblings ...) 2019-10-24 10:26 ` [PATCH 5/5] firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Hämmerle @ 2019-10-24 10:26 ` Thomas Hämmerle 2019-10-24 12:56 ` Michael Tretter 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 5 siblings, 1 reply; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-24 10:26 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> Port functions from xlnx-linux to get FPGA status and invoke bitstream loading. Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> --- arch/arm/mach-zynqmp/firmware-zynqmp.c | 47 ++++++++++++++++++++++ .../arm/mach-zynqmp/include/mach/firmware-zynqmp.h | 8 ++++ 2 files changed, 55 insertions(+) diff --git a/arch/arm/mach-zynqmp/firmware-zynqmp.c b/arch/arm/mach-zynqmp/firmware-zynqmp.c index f2187e9..18a1a51 100644 --- a/arch/arm/mach-zynqmp/firmware-zynqmp.c +++ b/arch/arm/mach-zynqmp/firmware-zynqmp.c @@ -508,6 +508,51 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, arg1, arg2, out); } +/** + * zynqmp_pm_fpga_load - Perform the fpga load + * @address: Address to write to + * @size: pl bitstream size + * @flags: Flags are used to specify the type of Bitstream file - + * defined in ZYNQMP_FPGA_BIT_*-macros + * + * This function provides access to xilfpga library to transfer + * the required bitstream into PL. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_load(u64 address, u32 size, u32 flags) +{ + if (!address || !size) + return -EINVAL; + + return zynqmp_pm_invoke_fn(PM_FPGA_LOAD, + lower_32_bits(address), upper_32_bits(address), + size, flags, NULL); +} + +/** + * zynqmp_pm_fpga_get_status - Read value from PCAP status register + * @value: Value to read + * + * This function provides access to the xilfpga library to get + * the PCAP status + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_get_status(u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret = 0; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); + *value = ret_payload[1]; + + return ret; +} + static const struct zynqmp_eemi_ops eemi_ops = { .get_api_version = zynqmp_pm_get_api_version, .query_data = zynqmp_pm_query_data, @@ -521,6 +566,8 @@ static const struct zynqmp_eemi_ops eemi_ops = { .clock_setparent = zynqmp_pm_clock_setparent, .clock_getparent = zynqmp_pm_clock_getparent, .ioctl = zynqmp_pm_ioctl, + .fpga_getstatus = zynqmp_pm_fpga_get_status, + .fpga_load = zynqmp_pm_fpga_load, }; /** diff --git a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h index 9e7a2e3..f19d73d 100644 --- a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h +++ b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h @@ -17,6 +17,12 @@ #define PAYLOAD_ARG_CNT 4 +#define ZYNQMP_FPGA_BIT_AUTH_DDR BIT(1) +#define ZYNQMP_FPGA_BIT_AUTH_OCM BIT(2) +#define ZYNQMP_FPGA_BIT_ENC_USR_KEY BIT(3) +#define ZYNQMP_FPGA_BIT_ENC_DEV_KEY BIT(4) +#define ZYNQMP_FPGA_BIT_ONLY_BIN BIT(5) + enum pm_ioctl_id { IOCTL_SET_PLL_FRAC_MODE = 8, IOCTL_GET_PLL_FRAC_MODE, @@ -61,6 +67,8 @@ struct zynqmp_eemi_ops { int (*clock_setparent)(u32 clock_id, u32 parent_id); int (*clock_getparent)(u32 clock_id, u32 *parent_id); int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out); + int (*fpga_getstatus)(u32 *status); + int (*fpga_load)(u64 address, u32 size, u32 flags); }; const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/5] firmware-zynqmp: extend driver with fpga relavant functions 2019-10-24 10:26 ` [PATCH 2/5] firmware-zynqmp: extend driver with fpga relavant functions Thomas Hämmerle @ 2019-10-24 12:56 ` Michael Tretter 0 siblings, 0 replies; 15+ messages in thread From: Michael Tretter @ 2019-10-24 12:56 UTC (permalink / raw) To: Thomas Hämmerle; +Cc: barebox On Thu, 24 Oct 2019 10:26:51 +0000, Thomas Hämmerle wrote: > From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> > > Port functions from xlnx-linux to get FPGA status and invoke bitstream > loading. > > Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> Reviewed-by: Michael Tretter <m.tretter@pengutronix.de> > --- > arch/arm/mach-zynqmp/firmware-zynqmp.c | 47 ++++++++++++++++++++++ > .../arm/mach-zynqmp/include/mach/firmware-zynqmp.h | 8 ++++ > 2 files changed, 55 insertions(+) > > diff --git a/arch/arm/mach-zynqmp/firmware-zynqmp.c b/arch/arm/mach-zynqmp/firmware-zynqmp.c > index f2187e9..18a1a51 100644 > --- a/arch/arm/mach-zynqmp/firmware-zynqmp.c > +++ b/arch/arm/mach-zynqmp/firmware-zynqmp.c > @@ -508,6 +508,51 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, > arg1, arg2, out); > } > > +/** > + * zynqmp_pm_fpga_load - Perform the fpga load > + * @address: Address to write to > + * @size: pl bitstream size > + * @flags: Flags are used to specify the type of Bitstream file - > + * defined in ZYNQMP_FPGA_BIT_*-macros > + * > + * This function provides access to xilfpga library to transfer > + * the required bitstream into PL. > + * > + * Return: Returns status, either success or error+reason > + */ > +static int zynqmp_pm_fpga_load(u64 address, u32 size, u32 flags) > +{ > + if (!address || !size) > + return -EINVAL; > + > + return zynqmp_pm_invoke_fn(PM_FPGA_LOAD, > + lower_32_bits(address), upper_32_bits(address), > + size, flags, NULL); > +} > + > +/** > + * zynqmp_pm_fpga_get_status - Read value from PCAP status register > + * @value: Value to read > + * > + * This function provides access to the xilfpga library to get > + * the PCAP status > + * > + * Return: Returns status, either success or error+reason > + */ > +static int zynqmp_pm_fpga_get_status(u32 *value) > +{ > + u32 ret_payload[PAYLOAD_ARG_CNT]; > + int ret = 0; > + > + if (!value) > + return -EINVAL; > + > + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); > + *value = ret_payload[1]; > + > + return ret; > +} > + > static const struct zynqmp_eemi_ops eemi_ops = { > .get_api_version = zynqmp_pm_get_api_version, > .query_data = zynqmp_pm_query_data, > @@ -521,6 +566,8 @@ static const struct zynqmp_eemi_ops eemi_ops = { > .clock_setparent = zynqmp_pm_clock_setparent, > .clock_getparent = zynqmp_pm_clock_getparent, > .ioctl = zynqmp_pm_ioctl, > + .fpga_getstatus = zynqmp_pm_fpga_get_status, > + .fpga_load = zynqmp_pm_fpga_load, > }; > > /** > diff --git a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h > index 9e7a2e3..f19d73d 100644 > --- a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h > +++ b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h > @@ -17,6 +17,12 @@ > > #define PAYLOAD_ARG_CNT 4 > > +#define ZYNQMP_FPGA_BIT_AUTH_DDR BIT(1) > +#define ZYNQMP_FPGA_BIT_AUTH_OCM BIT(2) > +#define ZYNQMP_FPGA_BIT_ENC_USR_KEY BIT(3) > +#define ZYNQMP_FPGA_BIT_ENC_DEV_KEY BIT(4) > +#define ZYNQMP_FPGA_BIT_ONLY_BIN BIT(5) > + I was not sure if the flags belong to the firmware interface or into the driver that uses the interface. While unlikely, other drivers might use the calls as well and, thus, here is the right place. Michael > enum pm_ioctl_id { > IOCTL_SET_PLL_FRAC_MODE = 8, > IOCTL_GET_PLL_FRAC_MODE, > @@ -61,6 +67,8 @@ struct zynqmp_eemi_ops { > int (*clock_setparent)(u32 clock_id, u32 parent_id); > int (*clock_getparent)(u32 clock_id, u32 *parent_id); > int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out); > + int (*fpga_getstatus)(u32 *status); > + int (*fpga_load)(u64 address, u32 size, u32 flags); > }; > > const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading 2019-10-24 10:26 [PATCH 0/5] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle ` (4 preceding siblings ...) 2019-10-24 10:26 ` [PATCH 2/5] firmware-zynqmp: extend driver with fpga relavant functions Thomas Hämmerle @ 2019-10-25 12:55 ` Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 1/4] firmware-zynqmp: add macros for PMU and trustzone firmware versions Thomas Hämmerle ` (4 more replies) 5 siblings, 5 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-25 12:55 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> Changes from v1: - remove dts commits - add commit for pmu and trustzone firmware virsion macro - split bin-header validation and byte-order detection - add description for bitstream Michael Tretter (1): firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Haemmerle (3): firmware-zynqmp: add macros for PMU and trustzone firmware versions firmware-zynqmp: extend driver with fpga relavant functions firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA arch/arm/configs/zynqmp_defconfig | 1 + arch/arm/mach-zynqmp/firmware-zynqmp.c | 68 +++- .../arm/mach-zynqmp/include/mach/firmware-zynqmp.h | 14 +- drivers/firmware/Kconfig | 7 + drivers/firmware/Makefile | 1 + drivers/firmware/zynqmp-fpga.c | 410 +++++++++++++++++++++ 6 files changed, 488 insertions(+), 13 deletions(-) create mode 100644 drivers/firmware/zynqmp-fpga.c -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 1/4] firmware-zynqmp: add macros for PMU and trustzone firmware versions 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle @ 2019-10-25 12:55 ` Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 2/4] firmware-zynqmp: extend driver with fpga relavant functions Thomas Hämmerle ` (3 subsequent siblings) 4 siblings, 0 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-25 12:55 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> --- arch/arm/mach-zynqmp/firmware-zynqmp.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-zynqmp/firmware-zynqmp.c b/arch/arm/mach-zynqmp/firmware-zynqmp.c index f2187e9..d91dcb0 100644 --- a/arch/arm/mach-zynqmp/firmware-zynqmp.c +++ b/arch/arm/mach-zynqmp/firmware-zynqmp.c @@ -18,17 +18,13 @@ #include <mach/firmware-zynqmp.h> -#define ZYNQMP_PM_VERSION_MAJOR 1 -#define ZYNQMP_PM_VERSION_MINOR 0 +#define ZYNQMP_PM_VERSION(MAJOR, MINOR) ((MAJOR << 16) | MINOR) +#define ZYNQMP_TZ_VERSION(MAJOR, MINOR) ((MAJOR << 16) | MINOR) -#define ZYNQMP_PM_VERSION ((ZYNQMP_PM_VERSION_MAJOR << 16) | \ - ZYNQMP_PM_VERSION_MINOR) - -#define ZYNQMP_TZ_VERSION_MAJOR 1 -#define ZYNQMP_TZ_VERSION_MINOR 0 - -#define ZYNQMP_TZ_VERSION ((ZYNQMP_TZ_VERSION_MAJOR << 16) | \ - ZYNQMP_TZ_VERSION_MINOR) +#define ZYNQMP_PM_VERSION_MAJOR 1 +#define ZYNQMP_PM_VERSION_MINOR 0 +#define ZYNQMP_TZ_VERSION_MAJOR 1 +#define ZYNQMP_TZ_VERSION_MINOR 0 /* SMC SIP service Call Function Identifier Prefix */ #define PM_SIP_SVC 0xC2000000 @@ -544,7 +540,8 @@ static int zynqmp_firmware_probe(struct device_d *dev) goto out; zynqmp_pm_get_api_version(&pm_api_version); - if (pm_api_version < ZYNQMP_PM_VERSION) { + if (pm_api_version < ZYNQMP_PM_VERSION(ZYNQMP_PM_VERSION_MAJOR, + ZYNQMP_PM_VERSION_MINOR)) { dev_err(dev, "Platform Management API version error." "Expected: v%d.%d - Found: v%d.%d\n", ZYNQMP_PM_VERSION_MAJOR, @@ -563,7 +560,8 @@ static int zynqmp_firmware_probe(struct device_d *dev) goto out; } - if (pm_tz_version < ZYNQMP_TZ_VERSION) { + if (pm_tz_version < ZYNQMP_TZ_VERSION(ZYNQMP_TZ_VERSION_MAJOR, + ZYNQMP_TZ_VERSION_MINOR)) { dev_err(dev, "Trustzone version error." "Expected: v%d.%d - Found: v%d.%d\n", ZYNQMP_TZ_VERSION_MAJOR, -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 2/4] firmware-zynqmp: extend driver with fpga relavant functions 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 1/4] firmware-zynqmp: add macros for PMU and trustzone firmware versions Thomas Hämmerle @ 2019-10-25 12:55 ` Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 4/4] firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Hämmerle ` (2 subsequent siblings) 4 siblings, 0 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-25 12:55 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> Port functions from xlnx-linux to get FPGA status and invoke bitstream loading. Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> --- arch/arm/mach-zynqmp/firmware-zynqmp.c | 48 +++++++++++++++++++++- .../arm/mach-zynqmp/include/mach/firmware-zynqmp.h | 14 ++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-zynqmp/firmware-zynqmp.c b/arch/arm/mach-zynqmp/firmware-zynqmp.c index d91dcb0..6123aa1 100644 --- a/arch/arm/mach-zynqmp/firmware-zynqmp.c +++ b/arch/arm/mach-zynqmp/firmware-zynqmp.c @@ -18,7 +18,6 @@ #include <mach/firmware-zynqmp.h> -#define ZYNQMP_PM_VERSION(MAJOR, MINOR) ((MAJOR << 16) | MINOR) #define ZYNQMP_TZ_VERSION(MAJOR, MINOR) ((MAJOR << 16) | MINOR) #define ZYNQMP_PM_VERSION_MAJOR 1 @@ -504,6 +503,51 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, arg1, arg2, out); } +/** + * zynqmp_pm_fpga_load - Perform the fpga load + * @address: Address to write to + * @size: pl bitstream size + * @flags: Flags are used to specify the type of Bitstream file - + * defined in ZYNQMP_FPGA_BIT_*-macros + * + * This function provides access to xilfpga library to transfer + * the required bitstream into PL. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_load(u64 address, u32 size, u32 flags) +{ + if (!address || !size) + return -EINVAL; + + return zynqmp_pm_invoke_fn(PM_FPGA_LOAD, + lower_32_bits(address), upper_32_bits(address), + size, flags, NULL); +} + +/** + * zynqmp_pm_fpga_get_status - Read value from PCAP status register + * @value: Value to read + * + * This function provides access to the xilfpga library to get + * the PCAP status + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_get_status(u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret = 0; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); + *value = ret_payload[1]; + + return ret; +} + static const struct zynqmp_eemi_ops eemi_ops = { .get_api_version = zynqmp_pm_get_api_version, .query_data = zynqmp_pm_query_data, @@ -517,6 +561,8 @@ static const struct zynqmp_eemi_ops eemi_ops = { .clock_setparent = zynqmp_pm_clock_setparent, .clock_getparent = zynqmp_pm_clock_getparent, .ioctl = zynqmp_pm_ioctl, + .fpga_getstatus = zynqmp_pm_fpga_get_status, + .fpga_load = zynqmp_pm_fpga_load, }; /** diff --git a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h index 9e7a2e3..a044822 100644 --- a/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h +++ b/arch/arm/mach-zynqmp/include/mach/firmware-zynqmp.h @@ -15,7 +15,17 @@ #ifndef FIRMWARE_ZYNQMP_H_ #define FIRMWARE_ZYNQMP_H_ -#define PAYLOAD_ARG_CNT 4 +#define PAYLOAD_ARG_CNT 4 + +#define ZYNQMP_PM_VERSION(MAJOR, MINOR) ((MAJOR << 16) | MINOR) + +#define ZYNQMP_FPGA_BIT_AUTH_DDR BIT(1) +#define ZYNQMP_FPGA_BIT_AUTH_OCM BIT(2) +#define ZYNQMP_FPGA_BIT_ENC_USR_KEY BIT(3) +#define ZYNQMP_FPGA_BIT_ENC_DEV_KEY BIT(4) +#define ZYNQMP_FPGA_BIT_ONLY_BIN BIT(5) + +#define ZYNQMP_PCAP_STATUS_FPGA_DONE BIT(3) enum pm_ioctl_id { IOCTL_SET_PLL_FRAC_MODE = 8, @@ -61,6 +71,8 @@ struct zynqmp_eemi_ops { int (*clock_setparent)(u32 clock_id, u32 parent_id); int (*clock_getparent)(u32 clock_id, u32 *parent_id); int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out); + int (*fpga_getstatus)(u32 *status); + int (*fpga_load)(u64 address, u32 size, u32 flags); }; const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 4/4] firmware: zynqmp-fpga: print Xilinx bitstream header 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 1/4] firmware-zynqmp: add macros for PMU and trustzone firmware versions Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 2/4] firmware-zynqmp: extend driver with fpga relavant functions Thomas Hämmerle @ 2019-10-25 12:55 ` Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 3/4] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Thomas Hämmerle 2019-10-28 11:00 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Sascha Hauer 4 siblings, 0 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-25 12:55 UTC (permalink / raw) To: barebox; +Cc: Michael Tretter From: Michael Tretter <m.tretter@pengutronix.de> The bitstream header has 5 fields, that start with a char for the type. Four fields have a big-ending length and a null-terminated string for the design name, the part number, and the date and time of creation. The last field is a big-endian 32 bit unsigned int for the size of the bitstream. Print this info when loading the bitstream. Signed-off-by: Michael Tretter <m.tretter@pengutronix.de> --- drivers/firmware/zynqmp-fpga.c | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c index 47862a7..8878658 100644 --- a/drivers/firmware/zynqmp-fpga.c +++ b/drivers/firmware/zynqmp-fpga.c @@ -45,6 +45,19 @@ struct fpgamgr { u32 features; }; +struct bs_header { + __be16 length; + u8 padding[9]; + __be16 size; + char entries[0]; +} __attribute__ ((packed)); + +struct bs_header_entry { + char type; + __be16 length; + char data[0]; +} __attribute__ ((packed)); + /* * Xilinx KU040 Bitstream Composition: * Bitstream can be provided with an optinal header (`struct bs_header`). @@ -143,6 +156,42 @@ static int get_header_length(const char *header, size_t size) return -EINVAL; } +static void zynqmp_fpga_show_header(const struct device_d *dev, + struct bs_header *header, size_t size) +{ + struct bs_header_entry *entry; + unsigned int i; + unsigned int length; + + for (i = 0; i < size - sizeof(*header); i += sizeof(*entry) + length) { + entry = (struct bs_header_entry *)&header->entries[i]; + length = __be16_to_cpu(entry->length); + + switch (entry->type) { + case 'a': + printf("Design: %s\n", entry->data); + break; + case 'b': + printf("Part number: %s\n", entry->data); + break; + case 'c': + printf("Date: %s\n", entry->data); + break; + case 'd': + printf("Time: %s\n", entry->data); + break; + case 'e': + /* Size entry does not have a length but is be32 int */ + printf("Size: %d bytes\n", + (length << 16) + (entry->data[0] << 8) + entry->data[1]); + return; + default: + dev_warn(dev, "Invalid header entry: %c", entry->type); + return; + } + } +} + static int fpgamgr_program_finish(struct firmware_handler *fh) { struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); @@ -167,6 +216,8 @@ static int fpgamgr_program_finish(struct firmware_handler *fh) status = header_length; goto err_free; } + zynqmp_fpga_show_header(&mgr->dev, + (struct bs_header *)mgr->buf, header_length); body = (u32 *)&mgr->buf[header_length]; body_length = mgr->size - header_length; -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 3/4] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle ` (2 preceding siblings ...) 2019-10-25 12:55 ` [PATCH v2 4/4] firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Hämmerle @ 2019-10-25 12:55 ` Thomas Hämmerle 2019-10-28 11:00 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Sascha Hauer 4 siblings, 0 replies; 15+ messages in thread From: Thomas Hämmerle @ 2019-10-25 12:55 UTC (permalink / raw) To: barebox; +Cc: Thomas Hämmerle From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> The driver provides functionalities to check and load a bitstream to FPGA. A boolean parameter to check if FPGA is already programmed is added. Signed-off-by: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> --- arch/arm/configs/zynqmp_defconfig | 1 + drivers/firmware/Kconfig | 7 + drivers/firmware/Makefile | 1 + drivers/firmware/zynqmp-fpga.c | 359 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 368 insertions(+) create mode 100644 drivers/firmware/zynqmp-fpga.c diff --git a/arch/arm/configs/zynqmp_defconfig b/arch/arm/configs/zynqmp_defconfig index 4dea964..834212e 100644 --- a/arch/arm/configs/zynqmp_defconfig +++ b/arch/arm/configs/zynqmp_defconfig @@ -35,4 +35,5 @@ CONFIG_CMD_OFTREE=y CONFIG_CMD_TIME=y CONFIG_DRIVER_SERIAL_CADENCE=y # CONFIG_SPI is not set +CONFIG_FIRMWARE_ZYNQMP_PL=y CONFIG_DIGEST=y diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 710b500..90b4c0a 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -14,4 +14,11 @@ config FIRMWARE_ALTERA_SOCFPGA bool "Altera SoCFPGA fpga loader" depends on ARCH_SOCFPGA select FIRMWARE + +config FIRMWARE_ZYNQMP_FPGA + bool "Xilinx ZynqMP FPGA loader" + depends on ARCH_ZYNQMP + select FIRMWARE + help + Load a bitstream to the PL of Zynq Ultrascale+ endmenu diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index c3a3c34..b162b08 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o +obj-$(CONFIG_FIRMWARE_ZYNQMP_FPGA) += zynqmp-fpga.o diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c new file mode 100644 index 0000000..47862a7 --- /dev/null +++ b/drivers/firmware/zynqmp-fpga.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Zynq MPSoC PL loading + * + * Copyright (c) 2018 Thomas Haemmerle <thomas.haemmerle@wolfvision.net> + * + * based on U-Boot zynqmppl code + * + * (C) Copyright 2015 - 2016, Xilinx, Inc, + * Michal Simek <michal.simek@xilinx.com> + * Siva Durga Prasad <siva.durga.paladugu@xilinx.com> * + */ + +#include <firmware.h> +#include <common.h> +#include <init.h> +#include <dma.h> +#include <mach/firmware-zynqmp.h> + +#define ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL BIT(0) +#define ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED BIT(1) + +#define ZYNQMP_PM_VERSION_1_0_FEATURES 0 +#define ZYNQMP_PM_VERSION_1_1_FEATURES (ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL | \ + ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED) + +#define DUMMY_WORD 0xFFFFFFFF +#define BUS_WIDTH_WORD_1 0x000000BB +#define BUS_WIDTH_WORD_2 0x11220044 +#define SYNC_WORD 0xAA995566 +#define SYNC_WORD_OFFS 20 + +enum xilinx_byte_order { + XILINX_BYTE_ORDER_BIT, + XILINX_BYTE_ORDER_BIN, +}; + +struct fpgamgr { + struct firmware_handler fh; + struct device_d dev; + const struct zynqmp_eemi_ops *eemi_ops; + int programmed; + char *buf; + size_t size; + u32 features; +}; + +/* + * Xilinx KU040 Bitstream Composition: + * Bitstream can be provided with an optinal header (`struct bs_header`). + * The true bitstream starts with the binary-header composed of 21 words: + * + * 1: 0xFFFFFFFF (Dummy pad word) + * ... + * 16: 0xFFFFFFFF (Dummy pad word) + * 17: 0x000000BB (Bus width auto detect word 1) + * 18: 0x11220044 (Bus width auto detect word 2) + * 19: 0xFFFFFFFF (Dummy pad word) + * 20: 0xFFFFFFFF (Dummy pad word) + * 21: 0xAA995566 (Sync word) + * + * Please refer to Xilinx UG570 (v1.11) September 30 2019, + * Chapter 9 Configuration Details - Bitstream Composition + * for further details! + */ +static const u32 bin_format[] = { + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + DUMMY_WORD, + BUS_WIDTH_WORD_1, + BUS_WIDTH_WORD_2, + DUMMY_WORD, + DUMMY_WORD, + SYNC_WORD, +}; + +static void copy_words_swapped(u32 *dst, const u32 *src, size_t size) +{ + int i; + + for (i = 0; i < size; i++) + dst[i] = __swab32(src[i]); +} + +static int get_byte_order(const u32 *bin_header, size_t size, + enum xilinx_byte_order *byte_order) +{ + if (size < sizeof(bin_format)) + return -EINVAL; + + if (bin_header[SYNC_WORD_OFFS] == SYNC_WORD) { + *byte_order = XILINX_BYTE_ORDER_BIT; + return 0; + } + + if (bin_header[SYNC_WORD_OFFS] == __swab32(SYNC_WORD)) { + *byte_order = XILINX_BYTE_ORDER_BIN; + return 0; + } + + return -EINVAL; +} + +static int is_bin_header_valid(const u32 *bin_header, size_t size, + enum xilinx_byte_order byte_order) +{ + int i; + + if (size < ARRAY_SIZE(bin_format)) + return 0; + + for (i = 0; i < ARRAY_SIZE(bin_format); i++) + if (bin_header != (byte_order == XILINX_BYTE_ORDER_BIT) ? + bin_format[i] : __swab32(bin_format[i])) + return 0; + + return 1; +} + +static int get_header_length(const char *header, size_t size) +{ + u32 *buf_u32; + int p; + + for (p = 0; p < size; p++) { + buf_u32 = (u32 *)&header[p]; + if (*buf_u32 == DUMMY_WORD) + return p; + } + return -EINVAL; +} + +static int fpgamgr_program_finish(struct firmware_handler *fh) +{ + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + char *buf_aligned; + u32 *buf_size = NULL; + u32 *body; + size_t body_length; + int header_length = 0; + enum xilinx_byte_order byte_order; + u64 addr; + int status = 0; + u8 flags = 0; + + if (!mgr->buf) { + status = -ENOBUFS; + dev_err(&mgr->dev, "buffer is NULL\n"); + goto err_free; + } + + header_length = get_header_length(mgr->buf, mgr->size); + if (header_length < 0) { + status = header_length; + goto err_free; + } + + body = (u32 *)&mgr->buf[header_length]; + body_length = mgr->size - header_length; + + status = get_byte_order(body, body_length, &byte_order); + if (status < 0) + goto err_free; + + if (!is_bin_header_valid(body, body_length, byte_order)) { + status = -EINVAL; + goto err_free; + } + + if (!(mgr->features & ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED)) { + buf_size = dma_alloc_coherent(sizeof(*buf_size), + DMA_ADDRESS_BROKEN); + if (!buf_size) { + status = -ENOBUFS; + goto err_free; + } + *buf_size = body_length; + } + + buf_aligned = dma_alloc_coherent(body_length, DMA_ADDRESS_BROKEN); + if (!buf_aligned) { + status = -ENOBUFS; + goto err_free; + } + + if (!(mgr->features & ZYNQMP_PM_FEATURE_BYTE_ORDER_IRREL) && + byte_order == XILINX_BYTE_ORDER_BIN) + copy_words_swapped((u32 *)buf_aligned, body, + body_length / sizeof(u32)); + else + memcpy((u32 *)buf_aligned, body, body_length); + + addr = (u64)buf_aligned; + + /* we do not provide a header */ + flags |= ZYNQMP_FPGA_BIT_ONLY_BIN; + + if (!(mgr->features & ZYNQMP_PM_FEATURE_SIZE_NOT_NEEDED) && buf_size) { + status = mgr->eemi_ops->fpga_load(addr, + (u32)(uintptr_t)buf_size, + flags); + dma_free_coherent(buf_size, 0, sizeof(*buf_size)); + } else { + status = mgr->eemi_ops->fpga_load(addr, (u32)(body_length), + flags); + } + + if (status < 0) + dev_err(&mgr->dev, "unable to load fpga\n"); + + dma_free_coherent(buf_aligned, 0, body_length); + + err_free: + free(mgr->buf); + + return status; +} + +static int fpgamgr_program_write_buf(struct firmware_handler *fh, + const void *buf, size_t size) +{ + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + + /* Since write() is called by copy_file, we only receive chuncks with + * size RW_BUF_SIZE of the bitstream. + * Buffer the chunks here and handle it in close() + */ + + mgr->buf = realloc(mgr->buf, mgr->size + size); + if (!mgr->buf) + return -ENOBUFS; + + memcpy(&(mgr->buf[mgr->size]), buf, size); + mgr->size += size; + + return 0; +} + +static int fpgamgr_program_start(struct firmware_handler *fh) +{ + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + + mgr->size = 0; + mgr->buf = NULL; + + return 0; +} + +static int programmed_get(struct param_d *p, void *priv) +{ + struct fpgamgr *mgr = priv; + u32 status = 0x00; + int ret = 0; + + ret = mgr->eemi_ops->fpga_getstatus(&status); + if (ret) + return ret; + + mgr->programmed = !!(status & ZYNQMP_PCAP_STATUS_FPGA_DONE); + + return 0; +} + +static int zynqmp_fpga_probe(struct device_d *dev) +{ + struct fpgamgr *mgr; + struct firmware_handler *fh; + const char *alias = of_alias_get(dev->device_node); + const char *model = NULL; + struct param_d *p; + u32 api_version; + int ret; + + mgr = xzalloc(sizeof(*mgr)); + fh = &mgr->fh; + + if (alias) + fh->id = xstrdup(alias); + else + fh->id = xstrdup("zynqmp-fpga-manager"); + + fh->open = fpgamgr_program_start; + fh->write = fpgamgr_program_write_buf; + fh->close = fpgamgr_program_finish; + of_property_read_string(dev->device_node, "compatible", &model); + if (model) + fh->model = xstrdup(model); + fh->dev = dev; + + mgr->eemi_ops = zynqmp_pm_get_eemi_ops(); + + ret = mgr->eemi_ops->get_api_version(&api_version); + if (ret) { + dev_err(&mgr->dev, "could not get API version\n"); + goto out; + } + + mgr->features = 0; + + if (api_version >= ZYNQMP_PM_VERSION(1, 1)) + mgr->features |= ZYNQMP_PM_VERSION_1_1_FEATURES; + + dev_dbg(dev, "Registering ZynqMP FPGA programmer\n"); + mgr->dev.id = DEVICE_ID_SINGLE; + dev_set_name(&mgr->dev, "zynqmp_fpga"); + mgr->dev.parent = dev; + ret = register_device(&mgr->dev); + if (ret) + goto out; + + p = dev_add_param_bool(&mgr->dev, "programmed", NULL, programmed_get, + &mgr->programmed, mgr); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + goto out_unreg; + } + + fh->dev = &mgr->dev; + ret = firmwaremgr_register(fh); + if (ret != 0) { + free(mgr); + goto out_unreg; + } + + return 0; +out_unreg: + unregister_device(&mgr->dev); +out: + free(fh->id); + free(mgr); + + return ret; +} + +static struct of_device_id zynqmpp_fpga_id_table[] = { + { + .compatible = "xlnx,zynqmp-pcap-fpga", + }, +}; + +static struct driver_d zynqmp_fpga_driver = { + .name = "zynqmp_fpga_manager", + .of_compatible = DRV_OF_COMPAT(zynqmpp_fpga_id_table), + .probe = zynqmp_fpga_probe, +}; +device_platform_driver(zynqmp_fpga_driver); -- 2.7.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle ` (3 preceding siblings ...) 2019-10-25 12:55 ` [PATCH v2 3/4] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Thomas Hämmerle @ 2019-10-28 11:00 ` Sascha Hauer 4 siblings, 0 replies; 15+ messages in thread From: Sascha Hauer @ 2019-10-28 11:00 UTC (permalink / raw) To: Thomas Hämmerle; +Cc: barebox On Fri, Oct 25, 2019 at 12:55:38PM +0000, Thomas Hämmerle wrote: > From: Thomas Haemmerle <thomas.haemmerle@wolfvision.net> > > Changes from v1: > - remove dts commits > - add commit for pmu and trustzone firmware virsion macro > - split bin-header validation and byte-order detection > - add description for bitstream > > Michael Tretter (1): > firmware: zynqmp-fpga: print Xilinx bitstream header > > Thomas Haemmerle (3): > firmware-zynqmp: add macros for PMU and trustzone firmware versions > firmware-zynqmp: extend driver with fpga relavant functions > firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Applied, thanks Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2019-10-28 11:00 UTC | newest] Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-10-24 10:26 [PATCH 0/5] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 1/5] ARM: zynqmp: dts: move firmware node to src tree Thomas Hämmerle 2019-10-24 12:56 ` Michael Tretter 2019-10-24 10:26 ` [PATCH 4/5] ARM: zynqmp: dts: move pcap " Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 3/5] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Thomas Hämmerle 2019-10-24 12:56 ` Michael Tretter 2019-10-24 10:26 ` [PATCH 5/5] firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Hämmerle 2019-10-24 10:26 ` [PATCH 2/5] firmware-zynqmp: extend driver with fpga relavant functions Thomas Hämmerle 2019-10-24 12:56 ` Michael Tretter 2019-10-25 12:55 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 1/4] firmware-zynqmp: add macros for PMU and trustzone firmware versions Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 2/4] firmware-zynqmp: extend driver with fpga relavant functions Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 4/4] firmware: zynqmp-fpga: print Xilinx bitstream header Thomas Hämmerle 2019-10-25 12:55 ` [PATCH v2 3/4] firmware: zynqmp-fpga: introduce driver to load bitstream to FPGA Thomas Hämmerle 2019-10-28 11:00 ` [PATCH v2 0/4] ARM: zynqmp: add support for bitstream loading Sascha Hauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox