diff options
-rw-r--r-- | configs/xilinx_zynqmp_ep_defconfig | 2 | ||||
-rw-r--r-- | configs/xilinx_zynqmp_zc1751_xm015_dc1_defconfig | 2 | ||||
-rw-r--r-- | configs/xilinx_zynqmp_zc1751_xm016_dc2_defconfig | 2 | ||||
-rw-r--r-- | configs/xilinx_zynqmp_zc1751_xm018_dc4_defconfig | 2 | ||||
-rw-r--r-- | configs/xilinx_zynqmp_zc1751_xm019_dc5_defconfig | 2 | ||||
-rw-r--r-- | configs/xilinx_zynqmp_zcu102_defconfig | 2 | ||||
-rw-r--r-- | configs/xilinx_zynqmp_zcu102_revB_defconfig | 2 | ||||
-rw-r--r-- | drivers/fpga/Kconfig | 17 | ||||
-rw-r--r-- | drivers/fpga/Makefile | 1 | ||||
-rw-r--r-- | drivers/fpga/xilinx.c | 6 | ||||
-rw-r--r-- | drivers/fpga/zynqmppl.c | 238 | ||||
-rw-r--r-- | include/xilinx.h | 2 | ||||
-rw-r--r-- | include/zynqmppl.h | 23 |
13 files changed, 301 insertions, 0 deletions
diff --git a/configs/xilinx_zynqmp_ep_defconfig b/configs/xilinx_zynqmp_ep_defconfig index 196eb6974e1..e7dc1c6f8db 100644 --- a/configs/xilinx_zynqmp_ep_defconfig +++ b/configs/xilinx_zynqmp_ep_defconfig @@ -46,6 +46,8 @@ CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_BLK=y +CONFIG_FPGA_XILINX=y +CONFIG_FPGA_ZYNQMPPL=y CONFIG_DM_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_CADENCE=y diff --git a/configs/xilinx_zynqmp_zc1751_xm015_dc1_defconfig b/configs/xilinx_zynqmp_zc1751_xm015_dc1_defconfig index a3fb226e918..6afacd2d91a 100644 --- a/configs/xilinx_zynqmp_zc1751_xm015_dc1_defconfig +++ b/configs/xilinx_zynqmp_zc1751_xm015_dc1_defconfig @@ -38,6 +38,8 @@ CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_BLK=y +CONFIG_FPGA_XILINX=y +CONFIG_FPGA_ZYNQMPPL=y CONFIG_DM_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_CADENCE=y diff --git a/configs/xilinx_zynqmp_zc1751_xm016_dc2_defconfig b/configs/xilinx_zynqmp_zc1751_xm016_dc2_defconfig index aec4f9b3ee6..4068c28818e 100644 --- a/configs/xilinx_zynqmp_zc1751_xm016_dc2_defconfig +++ b/configs/xilinx_zynqmp_zc1751_xm016_dc2_defconfig @@ -41,6 +41,8 @@ CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_BLK=y +CONFIG_FPGA_XILINX=y +CONFIG_FPGA_ZYNQMPPL=y CONFIG_DM_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_CADENCE=y diff --git a/configs/xilinx_zynqmp_zc1751_xm018_dc4_defconfig b/configs/xilinx_zynqmp_zc1751_xm018_dc4_defconfig index 0b2ebb1a54b..5116ff3c388 100644 --- a/configs/xilinx_zynqmp_zc1751_xm018_dc4_defconfig +++ b/configs/xilinx_zynqmp_zc1751_xm018_dc4_defconfig @@ -34,6 +34,8 @@ CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_BLK=y +CONFIG_FPGA_XILINX=y +CONFIG_FPGA_ZYNQMPPL=y CONFIG_DM_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_CADENCE=y diff --git a/configs/xilinx_zynqmp_zc1751_xm019_dc5_defconfig b/configs/xilinx_zynqmp_zc1751_xm019_dc5_defconfig index f570b2aa424..1a27bd06ab9 100644 --- a/configs/xilinx_zynqmp_zc1751_xm019_dc5_defconfig +++ b/configs/xilinx_zynqmp_zc1751_xm019_dc5_defconfig @@ -33,6 +33,8 @@ CONFIG_OF_EMBED=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_BLK=y +CONFIG_FPGA_XILINX=y +CONFIG_FPGA_ZYNQMPPL=y CONFIG_DM_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_CADENCE=y diff --git a/configs/xilinx_zynqmp_zcu102_defconfig b/configs/xilinx_zynqmp_zcu102_defconfig index 8fcb2fd3eed..78db9d9f13c 100644 --- a/configs/xilinx_zynqmp_zcu102_defconfig +++ b/configs/xilinx_zynqmp_zcu102_defconfig @@ -38,6 +38,8 @@ CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_BLK=y +CONFIG_FPGA_XILINX=y +CONFIG_FPGA_ZYNQMPPL=y CONFIG_DM_GPIO=y CONFIG_DM_MMC=y CONFIG_DM_MMC_OPS=y diff --git a/configs/xilinx_zynqmp_zcu102_revB_defconfig b/configs/xilinx_zynqmp_zcu102_revB_defconfig index b7ba599a091..62762820b79 100644 --- a/configs/xilinx_zynqmp_zcu102_revB_defconfig +++ b/configs/xilinx_zynqmp_zcu102_revB_defconfig @@ -38,6 +38,8 @@ CONFIG_NET_RANDOM_ETHADDR=y CONFIG_SPL_DM=y CONFIG_SPL_DM_SEQ_ALIAS=y CONFIG_BLK=y +CONFIG_FPGA_XILINX=y +CONFIG_FPGA_ZYNQMPPL=y CONFIG_DM_GPIO=y CONFIG_DM_MMC=y CONFIG_DM_MMC_OPS=y diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 1ba02f09825..f3f6bf7f674 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -1,3 +1,20 @@ menu "FPGA support" +config FPGA + bool + +config FPGA_XILINX + bool "Enable Xilinx FPGA drivers" + select FPGA + help + Enable Xilinx FPGA specific functions which includes bitstream + (in BIT format), fpga and device validation. + +config FPGA_ZYNQMPPL + bool "Enable Xilinx FPGA driver for ZynqMP" + depends on FPGA_XILINX + help + Enable FPGA driver for loading bitstream in BIT and BIN format + on Xilinx Zynq UltraScale+ (ZynqMP) device. + endmenu diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index fec3fecbdfb..777706f186d 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_FPGA_SPARTAN2) += spartan2.o obj-$(CONFIG_FPGA_SPARTAN3) += spartan3.o obj-$(CONFIG_FPGA_VIRTEX2) += virtex2.o obj-$(CONFIG_FPGA_ZYNQPL) += zynqpl.o +obj-$(CONFIG_FPGA_ZYNQMPPL) += zynqmppl.o obj-$(CONFIG_FPGA_XILINX) += xilinx.o obj-$(CONFIG_FPGA_LATTICE) += ivm_core.o lattice.o ifdef CONFIG_FPGA_ALTERA diff --git a/drivers/fpga/xilinx.c b/drivers/fpga/xilinx.c index d459a2f7a57..2cd0104d8b1 100644 --- a/drivers/fpga/xilinx.c +++ b/drivers/fpga/xilinx.c @@ -199,6 +199,9 @@ int xilinx_info(xilinx_desc *desc) case xilinx_zynq: printf("Zynq PL\n"); break; + case xilinx_zynqmp: + printf("ZynqMP PL\n"); + break; /* Add new family types here */ default: printf ("Unknown family type, %d\n", desc->family); @@ -227,6 +230,9 @@ int xilinx_info(xilinx_desc *desc) case devcfg: printf("Device configuration interface (Zynq)\n"); break; + case csu_dma: + printf("csu_dma configuration interface (ZynqMP)\n"); + break; /* Add new interface types here */ default: printf ("Unsupported interface type, %d\n", desc->iface); diff --git a/drivers/fpga/zynqmppl.c b/drivers/fpga/zynqmppl.c new file mode 100644 index 00000000000..23039c3eb2d --- /dev/null +++ b/drivers/fpga/zynqmppl.c @@ -0,0 +1,238 @@ +/* + * (C) Copyright 2015 - 2016, Xilinx, Inc, + * Michal Simek <michal.simek@xilinx.com> + * Siva Durga Prasad <siva.durga.paladugu@xilinx.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <console.h> +#include <common.h> +#include <zynqmppl.h> +#include <linux/sizes.h> + +#define DUMMY_WORD 0xffffffff + +/* Xilinx binary format header */ +static const u32 bin_format[] = { + DUMMY_WORD, /* Dummy words */ + 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, /* Sync word */ + 0x11220044, /* Sync word */ + DUMMY_WORD, + DUMMY_WORD, + 0xaa995566, /* Sync word */ +}; + +#define SWAP_NO 1 +#define SWAP_DONE 2 + +/* + * Load the whole word from unaligned buffer + * Keep in your mind that it is byte loading on little-endian system + */ +static u32 load_word(const void *buf, u32 swap) +{ + u32 word = 0; + u8 *bitc = (u8 *)buf; + int p; + + if (swap == SWAP_NO) { + for (p = 0; p < 4; p++) { + word <<= 8; + word |= bitc[p]; + } + } else { + for (p = 3; p >= 0; p--) { + word <<= 8; + word |= bitc[p]; + } + } + + return word; +} + +static u32 check_header(const void *buf) +{ + u32 i, pattern; + int swap = SWAP_NO; + u32 *test = (u32 *)buf; + + debug("%s: Let's check bitstream header\n", __func__); + + /* Checking that passing bin is not a bitstream */ + for (i = 0; i < ARRAY_SIZE(bin_format); i++) { + pattern = load_word(&test[i], swap); + + /* + * Bitstreams in binary format are swapped + * compare to regular bistream. + * Do not swap dummy word but if swap is done assume + * that parsing buffer is binary format + */ + if ((__swab32(pattern) != DUMMY_WORD) && + (__swab32(pattern) == bin_format[i])) { + swap = SWAP_DONE; + debug("%s: data swapped - let's swap\n", __func__); + } + + debug("%s: %d/%px: pattern %x/%x bin_format\n", __func__, i, + &test[i], pattern, bin_format[i]); + } + debug("%s: Found bitstream header at %px %s swapinng\n", __func__, + buf, swap == SWAP_NO ? "without" : "with"); + + return swap; +} + +static void *check_data(u8 *buf, size_t bsize, u32 *swap) +{ + u32 word, p = 0; /* possition */ + + /* Because buf doesn't need to be aligned let's read it by chars */ + for (p = 0; p < bsize; p++) { + word = load_word(&buf[p], SWAP_NO); + debug("%s: word %x %x/%px\n", __func__, word, p, &buf[p]); + + /* Find the first bitstream dummy word */ + if (word == DUMMY_WORD) { + debug("%s: Found dummy word at position %x/%px\n", + __func__, p, &buf[p]); + *swap = check_header(&buf[p]); + if (*swap) { + /* FIXME add full bitstream checking here */ + return &buf[p]; + } + } + /* Loop can be huge - support CTRL + C */ + if (ctrlc()) + return NULL; + } + return NULL; +} + +static ulong zynqmp_align_dma_buffer(u32 *buf, u32 len, u32 swap) +{ + u32 *new_buf; + u32 i; + + if ((ulong)buf != ALIGN((ulong)buf, ARCH_DMA_MINALIGN)) { + new_buf = (u32 *)ALIGN((ulong)buf, ARCH_DMA_MINALIGN); + + /* + * This might be dangerous but permits to flash if + * ARCH_DMA_MINALIGN is greater than header size + */ + if (new_buf > (u32 *)buf) { + debug("%s: Aligned buffer is after buffer start\n", + __func__); + new_buf -= ARCH_DMA_MINALIGN; + } + printf("%s: Align buffer at %px to %px(swap %d)\n", __func__, + buf, new_buf, swap); + + for (i = 0; i < (len/4); i++) + new_buf[i] = load_word(&buf[i], swap); + + buf = new_buf; + } else if (swap != SWAP_DONE) { + /* For bitstream which are aligned */ + u32 *new_buf = (u32 *)buf; + + printf("%s: Bitstream is not swapped(%d) - swap it\n", __func__, + swap); + + for (i = 0; i < (len/4); i++) + new_buf[i] = load_word(&buf[i], swap); + } + + return (ulong)buf; +} + +static int zynqmp_validate_bitstream(xilinx_desc *desc, const void *buf, + size_t bsize, u32 blocksize, u32 *swap) +{ + ulong *buf_start; + ulong diff; + + buf_start = check_data((u8 *)buf, blocksize, swap); + + if (!buf_start) + return FPGA_FAIL; + + /* Check if data is postpone from start */ + diff = (ulong)buf_start - (ulong)buf; + if (diff) { + printf("%s: Bitstream is not validated yet (diff %lx)\n", + __func__, diff); + return FPGA_FAIL; + } + + if ((ulong)buf < SZ_1M) { + printf("%s: Bitstream has to be placed up to 1MB (%px)\n", + __func__, buf); + return FPGA_FAIL; + } + + return 0; +} + +static int invoke_smc(ulong id, ulong reg0, ulong reg1, ulong reg2) +{ + struct pt_regs regs; + regs.regs[0] = id; + regs.regs[1] = reg0; + regs.regs[2] = reg1; + regs.regs[3] = reg2; + + smc_call(®s); + + return regs.regs[0]; +} + +static int zynqmp_load(xilinx_desc *desc, const void *buf, size_t bsize, + bitstream_type bstype) +{ + u32 swap; + ulong bin_buf, flags; + int ret; + + if (zynqmp_validate_bitstream(desc, buf, bsize, bsize, &swap)) + return FPGA_FAIL; + + bin_buf = zynqmp_align_dma_buffer((u32 *)buf, bsize, swap); + + debug("%s called!\n", __func__); + flush_dcache_range(bin_buf, bin_buf + bsize); + + if (bsize % 4) + bsize = bsize / 4 + 1; + else + bsize = bsize / 4; + + flags = (u32)bsize | ((u64)bstype << 32); + + ret = invoke_smc(ZYNQMP_SIP_SVC_PM_FPGA_LOAD, bin_buf, flags, 0); + if (ret) + debug("PL FPGA LOAD fail\n"); + + return ret; +} + +struct xilinx_fpga_op zynqmp_op = { + .load = zynqmp_load, +}; diff --git a/include/xilinx.h b/include/xilinx.h index aebcb3bfdf2..d2a2ea71e12 100644 --- a/include/xilinx.h +++ b/include/xilinx.h @@ -21,6 +21,7 @@ typedef enum { /* typedef xilinx_iface */ master_selectmap, /* master SelectMap (virtex2) */ slave_selectmap, /* slave SelectMap (virtex2) */ devcfg, /* devcfg interface (zynq) */ + csu_dma, /* csu_dma interface (zynqmp) */ max_xilinx_iface_type /* insert all new types before this */ } xilinx_iface; /* end, typedef xilinx_iface */ @@ -31,6 +32,7 @@ typedef enum { /* typedef xilinx_family */ xilinx_virtex2, /* Virtex2 Family */ xilinx_spartan3, /* Spartan-III Family */ xilinx_zynq, /* Zynq Family */ + xilinx_zynqmp, /* ZynqMP Family */ max_xilinx_type /* insert all new types before this */ } xilinx_family; /* end, typedef xilinx_family */ diff --git a/include/zynqmppl.h b/include/zynqmppl.h new file mode 100644 index 00000000000..002ee2aabbb --- /dev/null +++ b/include/zynqmppl.h @@ -0,0 +1,23 @@ +/* + * (C) Copyright 2015 Xilinx, Inc, + * Michal Simek <michal.simek@xilinx.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef _ZYNQMPPL_H_ +#define _ZYNQMPPL_H_ + +#include <xilinx.h> + +#define ZYNQMP_SIP_SVC_PM_FPGA_LOAD 0xC2000016 +#define ZYNQMP_FPGA_OP_INIT (1 << 0) +#define ZYNQMP_FPGA_OP_LOAD (1 << 1) +#define ZYNQMP_FPGA_OP_DONE (1 << 2) + +extern struct xilinx_fpga_op zynqmp_op; + +#define XILINX_ZYNQMP_DESC \ +{ xilinx_zynqmp, csu_dma, 1, &zynqmp_op, 0, &zynqmp_op } + +#endif /* _ZYNQMPPL_H_ */ |