This adds support to programm the PL part of the Zynq-7000 SoC, It adds the 'zynq_fpga_manager' so we can use
firmwareload -l firmwareload -t zynq-fpga-manager /mnt/mmc0.0/design_1_wrapper.bit to programm the PL. Signed-off-by: Michael Graichen <michael.graic...@hotmail.com> --- arch/arm/mach-zynq/Makefile | 2 +- arch/arm/mach-zynq/firmware-zynq.c | 145 ++++++ .../mach-zynq/include/mach/zynq7000-regs.h | 136 ++++++ drivers/firmware/Kconfig | 7 + drivers/firmware/Makefile | 1 + drivers/firmware/zynq-fpga.c | 422 ++++++++++++++++++ include/mach/zynq/firmware-zynq.h | 85 ++++ 7 files changed, 797 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-zynq/firmware-zynq.c create mode 100644 arch/arm/mach-zynq/include/mach/zynq7000-regs.h create mode 100644 drivers/firmware/zynq-fpga.c create mode 100644 include/mach/zynq/firmware-zynq.h diff --git a/arch/arm/mach-zynq/Makefile b/arch/arm/mach-zynq/Makefile index d5e94859a6..b974425bc9 100644 --- a/arch/arm/mach-zynq/Makefile +++ b/arch/arm/mach-zynq/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-y += zynq.o bootm-zynqimg.o +obj-y += zynq.o bootm-zynqimg.o firmware-zynq.o lwl-y += cpu_init.o diff --git a/arch/arm/mach-zynq/firmware-zynq.c b/arch/arm/mach-zynq/firmware-zynq.c new file mode 100644 index 0000000000..f3d164ea28 --- /dev/null +++ b/arch/arm/mach-zynq/firmware-zynq.c @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + */ +#include <common.h> +#include <init.h> +#include <linux/iopoll.h> +#include <mach/zynq/firmware-zynq.h> + +/* + * zynq_devc_fpga_load - Perform the fpga load + * @mgr: FPGA-Manager + * @address: Address to write to + * @size: PL bitstream size + * @flags: Flags - unused + * + * This function provides access to PCAP to transfer + * the required bitstream into PL. + * + * Return: Returns status, either success or error+reason + */ +static int zynq_devc_fpga_load(struct fpgamgr *mgr, u64 address, + u32 size, u32 flags) +{ + unsigned long reg; + int ret; + + if (!address || !size) + return -EINVAL; + + /* + * The Programming Seqenze, see ug585 (v.12.2) Juny 1, 2018 Chapter + * 6.4.2 on page 211 Configure the PL via PCAP Bridge Example for + * detailed information to this Sequenze + */ + + /* Enable the PCAP bridge and select PCAP for reconfiguration */ + reg = readl(mgr->regs + CTRL_OFFSET); + reg |= ( CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK ); + writel(reg, mgr->regs + CTRL_OFFSET); + + /* Clear the Interrupts */ + writel(0xffffffff, mgr->regs + INT_STS_OFFSET); + + /* Initialize the PL */ + reg = readl(mgr->regs + CTRL_OFFSET); + reg |= CTRL_PCFG_PROG_B_MASK; + writel(reg, mgr->regs + CTRL_OFFSET); + + reg = readl(mgr->regs + CTRL_OFFSET); + reg &= ~CTRL_PCFG_PROG_B_MASK; + writel(reg, mgr->regs + CTRL_OFFSET); + + ret = readl_poll_timeout(mgr->regs + STATUS_OFFSET, reg, + !(reg & STATUS_PCFG_INIT_MASK), 100 * USEC_PER_MSEC); + if (ret < 0) { + dev_err(&mgr->dev, "Timeout 1"); + return ret; + } + + reg = readl(mgr->regs + CTRL_OFFSET); + reg |= CTRL_PCFG_PROG_B_MASK; + writel(reg, mgr->regs + CTRL_OFFSET); + + /* Clear the Interrupts */ + writel(0xffffffff, mgr->regs + INT_STS_OFFSET); + + /* Ensure that the PL is ready for programming */ + ret = readl_poll_timeout(mgr->regs + STATUS_OFFSET, reg, + (reg & STATUS_PCFG_INIT_MASK), 100 * USEC_PER_MSEC); + if (ret < 0) { + dev_err(&mgr->dev, "Timeout 2"); + return ret; + } + + /* Check that there is room in the Command Queue */ + ret = readl_poll_timeout(mgr->regs + STATUS_OFFSET, reg, + !(reg & STATUS_DMA_CMD_Q_F_MASK), 100 * USEC_PER_MSEC); + if (ret < 0) { + dev_err(&mgr->dev, "Timeout 3"); + return ret; + } + + /* Disable the PCAP loopback */ + reg = readl(mgr->regs + MCTRL_OFFSET); + reg &= ~MCTRL_INT_PCAP_LPBK_MASK; + writel(reg, mgr->regs + MCTRL_OFFSET); + + /* Program the PCAP_2x clock divider */ + reg = readl(mgr->regs + CTRL_OFFSET); + reg &= ~CTRL_PCAP_RATE_EN_MASK; + writel(reg, mgr->regs + CTRL_OFFSET); + + /* Source Address: Location of bitstream */ + writel(address, mgr->regs + DMA_SRC_ADDR_OFFSET); + + /* Destination Address: 0xFFFF_FFFF */ + writel(0xffffffff, mgr->regs + DMA_DST_ADDR_OFFSET); + + /* Source Length: Total number of 32-bit words in the bitstream */ + writel((size >> 2), mgr->regs + DMA_SRC_LEN_OFFSET); + + /* Destination Length: Total number of 32-bit words in the bitstream */ + writel((size >> 2), mgr->regs + DMA_DEST_LEN_OFFSET); + + /* Wait for the DMA transfer to be done */ + ret = readl_poll_timeout(mgr->regs + INT_STS_OFFSET, reg, + (reg & INT_STS_D_P_DONE_MASK), 100 * USEC_PER_MSEC); + if (ret < 0) { + dev_err(&mgr->dev, "Timeout 4"); + return ret; + } + + /* Check for errors */ + if (reg & INT_STS_ERROR_FLAGS_MASK) { + dev_err(&mgr->dev, "interrupt status register (0x%04lx)\n", reg); + return -EIO; + } + + /* Wait for the DMA transfer to be done */ + ret = readl_poll_timeout(mgr->regs + INT_STS_OFFSET, reg, + (reg & INT_STS_DONE_INT_MASK), 100 * USEC_PER_MSEC); + if (ret < 0) { + dev_err(&mgr->dev, "Timeout 5"); + return ret; + } + + dev_info(&mgr->dev, "FPGA config done\n"); + + return 0; +} + +static const struct zynq_devc_ops devc_ops = { + .fpga_load = zynq_devc_fpga_load, +}; + +/** + * zynq_get_devc_ops - Get devc ops functions + * + * Return: Pointer of devc_ops structure + */ +const struct zynq_devc_ops *zynq_get_devc_ops(void) +{ + return &devc_ops; +} +EXPORT_SYMBOL_GPL(zynq_get_devc_ops); diff --git a/arch/arm/mach-zynq/include/mach/zynq7000-regs.h b/arch/arm/mach-zynq/include/mach/zynq7000-regs.h new file mode 100644 index 0000000000..9ca64a689d --- /dev/null +++ b/arch/arm/mach-zynq/include/mach/zynq7000-regs.h @@ -0,0 +1,136 @@ +/* + * (c) 2012 Steffen Trumtrar <s.trumt...@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#define ZYNQ_UART0_BASE_ADDR 0xE0000000 +#define ZYNQ_UART1_BASE_ADDR 0xE0001000 +#define ZYNQ_I2C0_BASE_ADDR 0xE0004000 +#define ZYNQ_I2C1_BASE_ADDR 0xE0005000 +#define ZYNQ_SPI0_BASE_ADDR 0xE0006000 +#define ZYNQ_SPI1_BASE_ADDR 0xE0007000 +#define ZYNQ_CAN0_BASE_ADDR 0xE0008000 +#define ZYNQ_CAN1_BASE_ADDR 0xE0009000 +#define ZYNQ_GPIO_BASE_ADDR 0xE000A000 +#define ZYNQ_GEM0_BASE_ADDR 0xE000B000 + +#define ZYNQ_SLCR_BASE 0xF8000000 +#define ZYNQ_SLCR_SCL (ZYNQ_SLCR_BASE + 0x000) +#define ZYNQ_SLCR_LOCK (ZYNQ_SLCR_BASE + 0x004) +#define ZYNQ_SLCR_UNLOCK (ZYNQ_SLCR_BASE + 0x008) +#define ZYNQ_SLCR_LOCKSTA (ZYNQ_SLCR_BASE + 0x00C) +#define ZYNQ_CLOCK_CTRL_BASE (ZYNQ_SLCR_BASE + 0x100) +#define ZYNQ_ARM_PLL_CTRL 0x000 +#define ZYNQ_DDR_PLL_CTRL 0x004 +#define ZYNQ_IO_PLL_CTRL 0x008 +#define ZYNQ_PLL_STATUS 0x00C +#define ZYNQ_ARM_PLL_CFG 0x010 +#define ZYNQ_DDR_PLL_CFG 0x014 +#define ZYNQ_IO_PLL_CFG 0x018 +#define ZYNQ_ARM_CLK_CTRL 0x020 +#define ZYNQ_DDR_CLK_CTRL 0x024 +#define ZYNQ_DCI_CLK_CTRL 0x028 +#define ZYNQ_APER_CLK_CTRL 0x02C +#define ZYNQ_USB0_CLK_CTRL 0x030 +#define ZYNQ_USB1_CLK_CTRL 0x034 +#define ZYNQ_GEM0_RCLK_CTRL 0x038 +#define ZYNQ_GEM1_RCLK_CTRL 0x03C +#define ZYNQ_GEM0_CLK_CTRL 0x040 +#define ZYNQ_GEM1_CLK_CTRL 0x044 +#define ZYNQ_SMC_CLK_CTRL 0x048 +#define ZYNQ_LQSPI_CLK_CTRL 0x04C +#define ZYNQ_SDIO_CLK_CTRL 0x050 +#define ZYNQ_UART_CLK_CTRL 0x054 +#define ZYNQ_SPI_CLK_CTRL 0x058 +#define ZYNQ_CAN_CLK_CTRL 0x05C +#define ZYNQ_CAN_MIOCLK_CTRL 0x060 +#define ZYNQ_DBG_CLK_CTRL 0x064 +#define ZYNQ_PCAP_CLK_CTRL 0x068 +#define ZYNQ_TOPSW_CLK_CTRL 0x06C +#define ZYNQ_FPGA0_CLK_CTRL 0x070 +#define ZYNQ_FPGA1_CLK_CTRL 0x080 +#define ZYNQ_FPGA2_CLK_CTRL 0x090 +#define ZYNQ_FPGA3_CLK_CTRL 0x0A0 +#define ZYNQ_CLK_621_TRUE 0x0C4 +#define ZYNQ_RST_CTRL_BASE (ZYNQ_SLCR_BASE + 0x200) +#define ZYNQ_SLCR_BOOT_MODE (ZYNQ_SLCR_BASE + 0x25C) +#define ZYNQ_PSS_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x000) +#define ZYNQ_DDR_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x004) +#define ZYNQ_TOPSW_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x008) +#define ZYNQ_DMAC_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x00C) +#define ZYNQ_USB_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x010) +#define ZYNQ_GEM_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x014) +#define ZYNQ_SDIO_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x018) +#define ZYNQ_SPI_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x01C) +#define ZYNQ_CAN_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x020) +#define ZYNQ_I2C_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x024) +#define ZYNQ_UART_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x028) +#define ZYNQ_GPIO_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x02C) +#define ZYNQ_LQSPI_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x030) +#define ZYNQ_SMC_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x034) +#define ZYNQ_OCM_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x038) +#define ZYNQ_DEVCI_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x03C) +#define ZYNQ_FPGA_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x040) +#define ZYNQ_A9_CPU_RST_CTRL (ZYNQ_RST_CTRL_BASE + 0x044) +#define ZYNQ_RS_AWDT_CTRL (ZYNQ_RST_CTRL_BASE + 0x04C) +#define ZYNQ_REBOOT_STATUS (ZYNQ_SLCR_BASE + 0x258) +#define ZYNQ_BOOT_MODE (ZYNQ_SLCR_BASE + 0x25C) +#define ZYNQ_APU_CTRL (ZYNQ_SLCR_BASE + 0x300) +#define ZYNQ_WDT_CLK_SEL (ZYNQ_SLCR_BASE + 0x304) +#define ZYNQ_PSS_IDCODE (ZYNQ_SLCR_BASE + 0x530) +#define ZYNQ_DDR_URGENT (ZYNQ_SLCR_BASE + 0x600) +#define ZYNQ_DDR_CAL_START (ZYNQ_SLCR_BASE + 0x60C) +#define ZYNQ_DDR_REF_START (ZYNQ_SLCR_BASE + 0x614) +#define ZYNQ_DDR_CMD_STA (ZYNQ_SLCR_BASE + 0x618) +#define ZYNQ_DDR_URGENT_SEL (ZYNQ_SLCR_BASE + 0x61C) +#define ZYNQ_DDR_DFI_STATUS (ZYNQ_SLCR_BASE + 0x620) +#define ZYNQ_MIO_BASE (ZYNQ_SLCR_BASE + 0x700) +#define ZYNQ_MIO_LOOPBACK (ZYNQ_MIO_BASE + 0x104) +#define ZYNQ_MIO_MST_TRI0 (ZYNQ_MIO_BASE + 0x10C) +#define ZYNQ_MIO_MST_TRI1 (ZYNQ_MIO_BASE + 0x110) +#define ZYNQ_SD0_WP_SEL (ZYNQ_SLCR_BASE + 0x830) +#define ZYNQ_SD1_WP_SEL (ZYNQ_SLCR_BASE + 0x834) +#define ZYNQ_LVL_SHIFTR_EN (ZYNQ_SLCR_BASE + 0x900) +#define ZYNQ_OCM_CFG (ZYNQ_SLCR_BASE + 0x910) +#define ZYNQ_GPIOB_BASE (ZYNQ_SLCR_BASE + 0xB00) +#define ZYNQ_GPIOB_CTRL (ZYNQ_GPIOB_BASE + 0x000) +#define ZYNQ_GPIOB_CFG_CMOS18 (ZYNQ_GPIOB_BASE + 0x004) +#define ZYNQ_GPIOB_CFG_CMOS25 (ZYNQ_GPIOB_BASE + 0x008) +#define ZYNQ_GPIOB_CFG_CMOS33 (ZYNQ_GPIOB_BASE + 0x00C) +#define ZYNQ_GPIOB_CFG_LVTTL (ZYNQ_GPIOB_BASE + 0x010) +#define ZYNQ_GPIOB_CFG_HSTL (ZYNQ_GPIOB_BASE + 0x014) +#define ZYNQ_GPIOB_DRV_BIAS_CTRL (ZYNQ_GPIOB_BASE + 0x018) +#define ZYNQ_DDRIOB_BASE (ZYNQ_SLCR_BASE + 0xB40) +#define ZYNQ_DDRIOB_ADDR0 (ZYNQ_DDRIOB_BASE + 0x000) +#define ZYNQ_DDRIOB_ADDR1 (ZYNQ_DDRIOB_BASE + 0x004) +#define ZYNQ_DDRIOB_DATA0 (ZYNQ_DDRIOB_BASE + 0x008) +#define ZYNQ_DDRIOB_DATA1 (ZYNQ_DDRIOB_BASE + 0x00C) +#define ZYNQ_DDRIOB_DIFF0 (ZYNQ_DDRIOB_BASE + 0x010) +#define ZYNQ_DDRIOB_DIFF1 (ZYNQ_DDRIOB_BASE + 0x014) +#define ZYNQ_DDRIOB_CLOCK (ZYNQ_DDRIOB_BASE + 0x018) +#define ZYNQ_DDRIOB_DRIVE_SLEW_ADDR (ZYNQ_DDRIOB_BASE + 0x01C) +#define ZYNQ_DDRIOB_DRIVE_SLEW_DATA (ZYNQ_DDRIOB_BASE + 0x020) +#define ZYNQ_DDRIOB_DRIVE_SLEW_DIFF (ZYNQ_DDRIOB_BASE + 0x024) +#define ZYNQ_DDRIOB_DRIVE_SLEW_CLOCK (ZYNQ_DDRIOB_BASE + 0x028) +#define ZYNQ_DDRIOB_DDR_CTRL (ZYNQ_DDRIOB_BASE + 0x02C) +#define ZYNQ_DDRIOB_DCI_CTRL (ZYNQ_DDRIOB_BASE + 0x030) +#define ZYNQ_DDRIOB_DCI_STATUS (ZYNQ_DDRIOB_BASE + 0x034) +#define ZYNQ_DEVCFG_MCTRL (ZYNQ_DEVCFG_BASE + 0x080) + +#define ZYNQ_TTC0_BASE_ADDR 0xF8001000 +#define ZYNQ_TTC1_BASE_ADDR 0xF8002000 + +#define ZYNQ_DDRC_BASE 0xF8006000 +#define ZYNQ_DEVCFG_BASE 0xf8007000 + +#define CORTEXA9_SCU_TIMER_BASE_ADDR 0xF8F00600 diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 264f7b2a5a..4f216bc7cb 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -20,6 +20,13 @@ config FIRMWARE_ALTERA_SOCFPGA depends on ARCH_SOCFPGA select FIRMWARE +config FIRMWARE_ZYNQ7000_FPGA + bool "Xilinx Zynq 7000 FPGA loader" + depends on ARCH_ZYNQ + select FIRMWARE + help + Load a bitstream to the PL of Zynq + config FIRMWARE_ZYNQMP_FPGA bool "Xilinx ZynqMP FPGA loader" depends on ARCH_ZYNQMP diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index d0ebe663ea..2032a830e4 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_SEMIHOSTING) += semihosting.o obj-$(CONFIG_FIRMWARE_ALTERA_SERIAL) += altera_serial.o obj-$(CONFIG_FIRMWARE_ALTERA_SOCFPGA) += socfpga.o socfpga_sdr.o +obj-$(CONFIG_FIRMWARE_ZYNQ7000_FPGA) += zynq-fpga.o obj-$(CONFIG_FIRMWARE_ZYNQMP_FPGA) += zynqmp-fpga.o obj-$(CONFIG_QEMU_FW_CFG) += qemu_fw_cfg.o obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o diff --git a/drivers/firmware/zynq-fpga.c b/drivers/firmware/zynq-fpga.c new file mode 100644 index 0000000000..4b543ddc66 --- /dev/null +++ b/drivers/firmware/zynq-fpga.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Zynq MPSoC and Zynq-7000 PL loading + * + * Copyright (c) 2018 Thomas Haemmerle <thomas.haemme...@wolfvision.net> + * + * based on U-Boot zynqmppl code + * + * (C) Copyright 2015 - 2016, Xilinx, Inc, + * Michal Simek <michal.si...@xilinx.com> + * Siva Durga Prasad <siva.durga.palad...@xilinx.com> * + */ + +#include <environment.h> +#include <firmware.h> +#include <common.h> +#include <init.h> +#include <dma.h> +#include <mach/zynq/firmware-zynq.h> +#include <mach/zynq/zynq7000-regs.h> + +enum xilinx_byte_order { + XILINX_BYTE_ORDER_BIT, + XILINX_BYTE_ORDER_BIN, +}; + +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)); + +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 < BIN_HEADER_LENGTH * sizeof(*bin_header)) + return -EINVAL; + + if (bin_header[SYNC_WORD_OFFSET] == SYNC_WORD) { + *byte_order = XILINX_BYTE_ORDER_BIT; + return 0; + } + + if (bin_header[SYNC_WORD_OFFSET] == __swab32(SYNC_WORD)) { + *byte_order = XILINX_BYTE_ORDER_BIN; + return 0; + } + + return -EINVAL; +} + +static bool is_bin_header_valid(const u32 *bin_header, size_t size, + enum xilinx_byte_order byte_order) +{ + size_t i; + + if (size < BIN_HEADER_LENGTH * sizeof(*bin_header)) + return false; + + for (i = 0; i < BIN_HEADER_LENGTH; i++, bin_header++) { + u32 current; + u32 expected; + + if (byte_order == XILINX_BYTE_ORDER_BIT) + current = *bin_header; + else + current = __swab32(*bin_header); + + switch (i) { + case BUS_WIDTH_AUTO_DETECT1_OFFSET: + expected = BUS_WIDTH_AUTO_DETECT1; + break; + case BUS_WIDTH_AUTO_DETECT2_OFFSET: + expected = BUS_WIDTH_AUTO_DETECT2; + break; + case SYNC_WORD_OFFSET: + expected = SYNC_WORD; + break; + default: + expected = DUMMY_WORD; + break; + } + + if (current != expected) + return false; + } + + return true; +} + +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 void zynq_fpga_show_header(const struct device_d *dev, + struct bs_header *header, size_t size) +{ + char * fpga_version_string = NULL; + 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); + asprintf(&fpga_version_string, "%s", entry->data); + break; + case 'c': + printf("Date: %s\n", entry->data); + asprintf(&fpga_version_string, "%s_%s", fpga_version_string, entry->data); + break; + case 'd': + printf("Time: %s\n", entry->data); + asprintf(&fpga_version_string, "%s_%s", fpga_version_string, 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]); + setenv("fpga-version", fpga_version_string); + return; + default: + dev_warn(dev, "Invalid header entry: %c", entry->type); + return; + } + } + + return; +} + +static int fpgamgr_program_finish(struct firmware_handler *fh) +{ + struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + char *buf_aligned; + u32 *body; + size_t body_length; + int header_length = 0; + enum xilinx_byte_order byte_order; + u64 addr; + int status = 0; +#ifdef CONFIG_FIRMWARE_ZYNQMP_FPGA + u32 *buf_size = NULL; + u8 flags = 0; +#endif + 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; + } + zynq_fpga_show_header(&mgr->dev, + (struct bs_header *)mgr->buf, header_length); + + 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)) { + dev_err(&mgr->dev, "Invalid bitstream header\n"); + status = -EINVAL; + goto err_free; + } + + buf_aligned = dma_alloc_coherent(DMA_DEVICE_BROKEN, body_length, DMA_ADDRESS_BROKEN); + if (!buf_aligned) { + status = -ENOBUFS; + goto err_free; + } + +#ifdef CONFIG_FIRMWARE_ZYNQMP_FPGA + 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; + } + + 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); + } +#endif +#ifdef CONFIG_FIRMWARE_ZYNQ7000_FPGA + /* UG585 (v1.12.2) July 1, 2018 Chapter 6.4.3 + * In all modes, the DMA transactions must be 64-byte aligned to prevent + * accidently crossing a 4K byte boundary. + */ + if(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 = (u32)buf_aligned; + + writel(0x0000DF0D, ZYNQ_SLCR_UNLOCK); + writel(0x0000000f, ZYNQ_SLCR_BASE + 0x240); // assert FPGA resets + + writel(0x00000000, ZYNQ_SLCR_BASE + 0x900); // disable levelshifter + writel(0x0000000a, ZYNQ_SLCR_BASE + 0x900); // enable levelshifter PS-PL + + status = mgr->devc_ops->fpga_load(mgr, addr, (u32)(body_length), 0); + + writel(0x0000000f, ZYNQ_SLCR_BASE + 0x900); // enable all levelshifter + writel(0x00000000, ZYNQ_SLCR_BASE + 0x240); // deassert FPGA resets + + writel(0x0000767B, ZYNQ_SLCR_LOCK); +#endif + if (status < 0) + dev_err(&mgr->dev, "unable to load fpga\n"); + + dma_free_coherent(DMA_DEVICE_BROKEN, 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) +{ +#ifdef CONFIG_FIRMWARE_ZYNQMP_FPGA + 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); +#endif + return 0; +} + +static int zynq_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; +#ifdef CONFIG_FIRMWARE_ZYNQMP_FPGA + u32 api_version; +#endif +#ifdef CONFIG_FIRMWARE_ZYNQ7000_FPGA + struct resource *iores; +#endif + int ret; + + mgr = xzalloc(sizeof(*mgr)); + fh = &mgr->fh; + + if (alias) + fh->id = xstrdup(alias); + else + fh->id = xstrdup("zynq-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; + +#ifdef CONFIG_FIRMWARE_ZYNQMP_FPGA + 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; + } + if (api_version >= ZYNQMP_PM_VERSION(1, 1)) + mgr->features |= ZYNQMP_PM_VERSION_1_1_FEATURES; +#endif +#ifdef CONFIG_FIRMWARE_ZYNQ7000_FPGA + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + goto out; + } + mgr->regs = IOMEM(iores->start); + mgr->devc_ops = zynq_get_devc_ops(); + /* Unlock DevC in case BootROM did not do it */ + writel(DEVC_UNLOCK_CODE, mgr->regs + UNLOCK_OFFSET); +#endif + mgr->features = 0; + + dev_dbg(dev, "Registering Zynq FPGA programmer\n"); + mgr->dev.id = DEVICE_ID_SINGLE; + dev_set_name(&mgr->dev, "zynq_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 zynq_fpga_id_table[] = { + { .compatible = "xlnx,zynqmp-pcap-fpga" }, + { .compatible = "xlnx,zynq-devcfg-1.0" }, + { /* sentinel */ } +}; + +static struct driver_d zynq_fpga_driver = { + .name = "zynq_fpga_manager", + .of_compatible = DRV_OF_COMPAT(zynq_fpga_id_table), + .probe = zynq_fpga_probe, +}; +device_platform_driver(zynq_fpga_driver); diff --git a/include/mach/zynq/firmware-zynq.h b/include/mach/zynq/firmware-zynq.h new file mode 100644 index 0000000000..217b1e9b2e --- /dev/null +++ b/include/mach/zynq/firmware-zynq.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Xilinx Zynq Firmware layer + * + */ + +#ifndef FIRMWARE_ZYNQ_H_ +#define FIRMWARE_ZYNQ_H_ + +#include <firmware.h> + +#define CTRL_OFFSET 0x00 +#define LOCK_OFFSET 0x04 +#define INT_STS_OFFSET 0x0c +#define INT_MASK_OFFSET 0x10 +#define STATUS_OFFSET 0x14 +#define DMA_SRC_ADDR_OFFSET 0x18 +#define DMA_DST_ADDR_OFFSET 0x1c +#define DMA_SRC_LEN_OFFSET 0x20 +#define DMA_DEST_LEN_OFFSET 0x24 +#define UNLOCK_OFFSET 0x34 +#define MCTRL_OFFSET 0x80 + +#define CTRL_PCFG_PROG_B_MASK BIT(30) +#define CTRL_PCAP_PR_MASK BIT(27) +#define CTRL_PCAP_MODE_MASK BIT(26) +#define CTRL_PCAP_RATE_EN_MASK BIT(25) + +#define STATUS_DMA_CMD_Q_F_MASK BIT(31) +#define STATUS_PCFG_INIT_MASK BIT(4) + +#define INT_STS_D_P_DONE_MASK BIT(12) +#define INT_STS_DONE_INT_MASK BIT(2) +#define INT_STS_ERROR_FLAGS_MASK 0x00f4c860 + +#define MCTRL_INT_PCAP_LPBK_MASK BIT(4) + +/* + * Xilinx 7-Series Bitstream Composition: + * + * Bitstream can be provided with an optinal header (`struct bs_header`). + * The true bitstream starts with the binary-header composed of 13 words: + * + * 0: 0xFFFFFFFF (Dummy pad word) + * ... + * 7: 0xFFFFFFFF (Dummy pad word) + * 8: 0x000000BB (Bus width auto detect word 1) + * 9: 0x11220044 (Bus width auto detect word 2) + * 10: 0xFFFFFFFF (Dummy pad word) + * 11: 0xFFFFFFFF (Dummy pad word) + * 12: 0xAA995566 (Sync word) + * + * See Xilinx UG470 (v1.13.1) August 20 2018, Chapter 5 "Configuration + * Details - Bitstream Composition" for further details. + */ + +#define DUMMY_WORD 0xFFFFFFFF +#define BUS_WIDTH_AUTO_DETECT1_OFFSET 8 +#define BUS_WIDTH_AUTO_DETECT1 0x000000BB +#define BUS_WIDTH_AUTO_DETECT2_OFFSET 9 +#define BUS_WIDTH_AUTO_DETECT2 0x11220044 +#define SYNC_WORD_OFFSET 12 +#define SYNC_WORD 0xAA995566 +#define BIN_HEADER_LENGTH 13 + +#define DEVC_UNLOCK_CODE 0x757bdf0d + +struct fpgamgr { + struct firmware_handler fh; + struct device_d dev; + void __iomem *regs; + const struct zynq_devc_ops *devc_ops; + int programmed; + char *buf; + size_t size; + u32 features; +}; + +struct zynq_devc_ops { + int (*fpga_load)(struct fpgamgr *mgr, u64 address, u32 size, u32 flags); +}; + +const struct zynq_devc_ops *zynq_get_devc_ops(void); + +#endif /* FIRMWARE_ZYNQ_H_ */ -- 2.43.0