From 06e4f36b4b8f8b410a4648879950e18edfd2f1a2 Mon Sep 17 00:00:00 2001 From: Mateusz Sierszulski Date: Mon, 14 Feb 2022 11:45:17 +0100 Subject: [PATCH] fpga controller: drivers: add ZynqMP driver This commit adds support for fpga driver on ZynqMP SoC. Signed-off-by: Mateusz Sierszulski --- boards/arm/mercury_xu/mercury_xu.dts | 1 + drivers/fpga/CMakeLists.txt | 1 + drivers/fpga/Kconfig | 3 +- drivers/fpga/Kconfig.zynqmp | 9 + drivers/fpga/fpga_zynqmp.c | 326 +++++++++++++++++++++++++++ drivers/fpga/fpga_zynqmp.h | 71 ++++++ dts/bindings/fpga/xlnx,fpga.yaml | 6 + 7 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 drivers/fpga/Kconfig.zynqmp create mode 100644 drivers/fpga/fpga_zynqmp.c create mode 100644 drivers/fpga/fpga_zynqmp.h create mode 100644 dts/bindings/fpga/xlnx,fpga.yaml diff --git a/boards/arm/mercury_xu/mercury_xu.dts b/boards/arm/mercury_xu/mercury_xu.dts index 518f18d85af..c58e48e31f9 100644 --- a/boards/arm/mercury_xu/mercury_xu.dts +++ b/boards/arm/mercury_xu/mercury_xu.dts @@ -19,6 +19,7 @@ fpga0: fpga { status = "okay"; + compatible = "xlnx,fpga"; }; }; diff --git a/drivers/fpga/CMakeLists.txt b/drivers/fpga/CMakeLists.txt index c4893764864..092228bc30d 100644 --- a/drivers/fpga/CMakeLists.txt +++ b/drivers/fpga/CMakeLists.txt @@ -4,3 +4,4 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_EOS_S3_FPGA fpga_eos_s3.c) zephyr_library_sources_ifdef(CONFIG_FPGA_SHELL fpga_shell.c) +zephyr_library_sources_ifdef(CONFIG_ZYNQMP_FPGA fpga_zynqmp.c) diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index e6159786bbb..da3117f479e 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -1,6 +1,6 @@ # FPGA driver configuration options -# Copyright (c) 2021 Antmicro +# Copyright (c) 2021-2022 Antmicro # SPDX-License-Identifier: Apache-2.0 menuconfig FPGA @@ -21,5 +21,6 @@ config FPGA_SHELL Enable FPGA Shell support. source "drivers/fpga/Kconfig.eos_s3" +source "drivers/fpga/Kconfig.zynqmp" endif # FPGA diff --git a/drivers/fpga/Kconfig.zynqmp b/drivers/fpga/Kconfig.zynqmp new file mode 100644 index 00000000000..4536d3c1d3e --- /dev/null +++ b/drivers/fpga/Kconfig.zynqmp @@ -0,0 +1,9 @@ +# FPGA ZYNQMP driver configuration options + +# Copyright (c) 2021-2022 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +config ZYNQMP_FPGA + bool "ZYNQMP fpga driver" + help + Enable ZYNQMP FPGA driver. diff --git a/drivers/fpga/fpga_zynqmp.c b/drivers/fpga/fpga_zynqmp.c new file mode 100644 index 00000000000..8dce633eb07 --- /dev/null +++ b/drivers/fpga/fpga_zynqmp.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2021-2022 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT xlnx_fpga + +#include +#include +#include "fpga_zynqmp.h" +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(fpga_zynqmp); + +static void power_up_fpga(void) +{ + PMU_GLOBAL_PWRUP_EN = PWR_PL_MASK; + PMU_REQ_PWRUP_TRIG = PWR_PL_MASK; + + while (PWR_STATUS & PWR_PL_MASK) { + }; +} + +struct zynqmp_fpga_data { + char FPGA_info[16]; +}; + +static void update_part_name(const struct device *dev) +{ + struct zynqmp_fpga_data *data = dev->data; + + int zu_number = 0; + + switch (IDCODE & IDCODE_MASK) { + case ZU2_IDCODE: + zu_number = 2; + break; + case ZU3_IDCODE: + zu_number = 3; + break; + case ZU4_IDCODE: + zu_number = 4; + break; + case ZU5_IDCODE: + zu_number = 5; + break; + case ZU6_IDCODE: + zu_number = 6; + break; + case ZU7_IDCODE: + zu_number = 7; + break; + case ZU9_IDCODE: + zu_number = 9; + break; + case ZU11_IDCODE: + zu_number = 11; + break; + case ZU15_IDCODE: + zu_number = 15; + break; + case ZU17_IDCODE: + zu_number = 17; + break; + case ZU19_IDCODE: + zu_number = 19; + break; + case ZU21_IDCODE: + zu_number = 21; + break; + case ZU25_IDCODE: + zu_number = 25; + break; + case ZU27_IDCODE: + zu_number = 27; + break; + case ZU28_IDCODE: + zu_number = 28; + break; + case ZU29_IDCODE: + zu_number = 29; + break; + case ZU39_IDCODE: + zu_number = 39; + break; + case ZU43_IDCODE: + zu_number = 43; + break; + case ZU46_IDCODE: + zu_number = 46; + break; + case ZU47_IDCODE: + zu_number = 47; + break; + case ZU48_IDCODE: + zu_number = 48; + break; + case ZU49_IDCODE: + zu_number = 49; + break; + } + + if (zu_number == 0) { + snprintf(data->FPGA_info, sizeof(data->FPGA_info), "unknown"); + } else { + snprintf(data->FPGA_info, sizeof(data->FPGA_info), "Part name: ZU%d", zu_number); + } + +} + +/* + * This function is responsible for shifting the bitstream + * by its header and extracting information from this header. + * The bitstream header has 5 sections starting with the letters a,b,c... + * Each section has the following structure: + * [key][length of data][data] + */ +static uint32_t *parse_header(const struct device *dev, uint32_t *image_ptr, + uint32_t *img_size) +{ + unsigned char *header = (unsigned char *)image_ptr; + + uint32_t length = XLNX_BITSTREAM_SECTION_LENGTH(header); + + /* shift to the next section*/ + header += 0x4U + length; + + if (*header++ != 'a') { + LOG_ERR("Incorrect bitstream format"); + return NULL; + } + + length = XLNX_BITSTREAM_SECTION_LENGTH(header); + /* shift to the data section*/ + header += 0x2U; + + LOG_DBG("Design name = %s", header); + + header += length; + + if (*header++ != 'b') { + LOG_ERR("Incorrect bitstream format"); + return NULL; + } + + length = XLNX_BITSTREAM_SECTION_LENGTH(header); + /* shift to the data section*/ + header += 0x2U; + LOG_DBG("Part name = %s", header); + + header += length; + + if (*header++ != 'c') { + LOG_ERR("Incorrect bitstream format"); + return NULL; + } + + length = XLNX_BITSTREAM_SECTION_LENGTH(header); + /* shift to the data section*/ + header += 0x2U; + + LOG_DBG("Date = %s", header); + + header += length; + + if (*header++ != 'd') { + LOG_ERR("Incorrect bitstream format"); + return NULL; + } + + length = XLNX_BITSTREAM_SECTION_LENGTH(header); + /* shift to the data section*/ + header += 0x2U; + + LOG_DBG("Time = %s", header); + + header += length; + + if (*header++ != 'e') { + LOG_ERR("Incorrect bitstream format"); + return NULL; + } + + /* + * The last section is the raw bitstream. + * It is preceded by its size, which is needed for DMA transfer. + */ + *img_size = + ((uint32_t)*header << 24) | ((uint32_t) *(header + 1) << 16) | + ((uint32_t) *(header + 2) << 8) | ((uint32_t) *(header + 3)); + + return (uint32_t *)header; +} + +static int csudma_transfer(uint32_t size) +{ + /* setup the source DMA channel */ + CSUDMA_SRC_ADDR = (uint32_t)BITSTREAM & CSUDMA_SRC_ADDR_MASK; + CSUDMA_SRC_ADDR_MSB = 0; + CSUDMA_SRC_SIZE = size << CSUDMA_SRC_SIZE_SHIFT; + + /* wait for the SRC_DMA to complete */ + while ((CSUDMA_SRC_I_STS & CSUDMA_I_STS_DONE_MASK) != CSUDMA_I_STS_DONE_MASK) { + }; + + /* acknowledge the transfer has completed */ + CSUDMA_SRC_I_STS = CSUDMA_I_STS_DONE_MASK; + + return 0; +} + +static int wait_for_done(void) +{ + /* wait for PCAP PL_DONE */ + while ((PCAP_STATUS & PCAP_PL_DONE_MASK) != PCAP_PL_DONE_MASK) { + }; + + PCAP_RESET = PCAP_RESET_MASK; + power_up_fpga(); + + return 0; +} + +static enum FPGA_status zynqmp_fpga_get_status(const struct device *dev) +{ + ARG_UNUSED(dev); + + if ((PCAP_STATUS & PCAP_PL_INIT_MASK) && (PCAP_STATUS & PCAP_PL_DONE_MASK)) { + return FPGA_STATUS_ACTIVE; + } else { + return FPGA_STATUS_INACTIVE; + } +} + +static const char *zynqmp_fpga_get_info(const struct device *dev) +{ + struct zynqmp_fpga_data *data = dev->data; + + return data->FPGA_info; +} + +static int zynqmp_fpga_reset(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* Reset PL */ + PCAP_PROG = PCAP_PROG_RESET_MASK; + PCAP_PROG = ~PCAP_PROG_RESET_MASK; + + while ((PCAP_STATUS & PCAP_CFG_RESET) != PCAP_CFG_RESET) { + }; + + return 0; +} + +static int init_pcap(const struct device *dev) +{ + /* take PCAP out of Reset */ + PCAP_RESET = ~PCAP_RESET_MASK; + + /* select PCAP mode and change PCAP to write mode */ + PCAP_CTRL = PCAP_PR_MASK; + PCAP_RDWR = PCAP_WRITE_MASK; + + power_up_fpga(); + + /* setup the SSS */ + CSU_SSS_CFG = PCAP_PCAP_SSS_MASK; + + zynqmp_fpga_reset(dev); + + /* wait for pl init */ + while ((PCAP_STATUS & PCAP_PL_INIT_MASK) != PCAP_PL_INIT_MASK) { + }; + + return 0; +} + +static int zynqmp_fpga_load(const struct device *dev, uint32_t *image_ptr, + uint32_t img_size) +{ + uint32_t *addr = parse_header(dev, image_ptr, &img_size); + + if (addr == NULL) { + LOG_ERR("Failed to read bitstream"); + return -EINVAL; + } + + for (int i = 0; i < (img_size / 4); i++) { + *(BITSTREAM + i) = __bswap_32(*(addr + i)); + } + + init_pcap(dev); + csudma_transfer(img_size); + wait_for_done(); + + return 0; +} + +static int zynqmp_fpga_init(const struct device *dev) +{ + /* turn on PCAP CLK */ + PCAP_CLK_CTRL = PCAP_CLK_CTRL | PCAP_CLKACT_MASK; + + update_part_name(dev); + + return 0; +} + +static struct zynqmp_fpga_data fpga_data; + +static const struct fpga_driver_api zynqmp_api = { + .reset = zynqmp_fpga_reset, + .load = zynqmp_fpga_load, + .get_status = zynqmp_fpga_get_status, + .get_info = zynqmp_fpga_get_info +}; + +DEVICE_DT_INST_DEFINE(0, &zynqmp_fpga_init, NULL, &fpga_data, NULL, + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &zynqmp_api); diff --git a/drivers/fpga/fpga_zynqmp.h b/drivers/fpga/fpga_zynqmp.h new file mode 100644 index 00000000000..87caafa44a8 --- /dev/null +++ b/drivers/fpga/fpga_zynqmp.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021-2022 Antmicro + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_FPGA_ZYNQMP_H +#define ZEPHYR_DRIVERS_FPGA_ZYNQMP_H + +#define PCAP_STATUS (*(volatile uint32_t *) (0xFFCA3010)) +#define PCAP_RESET (*(volatile uint32_t *) (0xFFCA300C)) +#define PCAP_CTRL (*(volatile uint32_t *) (0xFFCA3008)) +#define PCAP_RDWR (*(volatile uint32_t *) (0xFFCA3004)) +#define PMU_REQ_PWRUP_TRIG (*(volatile uint32_t *) (0xFFD80120)) +#define PCAP_PROG (*(volatile uint32_t *) (0xFFCA3000)) +#define CSU_SSS_CFG (*(volatile uint32_t *) (0xFFCA0008)) +#define CSUDMA_SRC_ADDR (*(volatile uint32_t *) (0xFFC80000)) +#define CSUDMA_SRC_SIZE (*(volatile uint32_t *) (0xFFC80004)) +#define CSUDMA_SRC_I_STS (*(volatile uint32_t *) (0xFFC80014)) +#define CSUDMA_SRC_ADDR_MSB (*(volatile uint32_t *) (0xFFC80028)) +#define PWR_STATUS (*(volatile uint32_t *) (0xFFD80110)) +#define PMU_GLOBAL_ISO_STATUS (*(volatile uint32_t *) (0xFFD80310)) +#define PMU_GLOBAL_PWRUP_EN (*(volatile uint32_t *) (0xFFD80118)) +#define PCAP_CLK_CTRL (*(volatile uint32_t *) (0xFF5E00A4)) +#define PMU_GLOBAL_ISO_INT_EN (*(volatile uint32_t *) (0xFFD80318)) +#define PMU_GLOBAL_ISO_TRIG (*(volatile uint32_t *) (0xFFD80320)) +#define IDCODE (*(volatile uint32_t *) (0xFFCA0040)) +#define BITSTREAM ((volatile uint32_t *) (0x01000000)) + +#define PWR_PL_MASK 0x800000U +#define ISO_MASK 0x4U +#define PCAP_RESET_MASK 0x1U +#define PCAP_PROG_RESET_MASK 0x0U +#define PCAP_PR_MASK 0x1U +#define PCAP_WRITE_MASK 0x0U +#define PCAP_PL_INIT_MASK 0x4U +#define PCAP_CLKACT_MASK 0x1000000U +#define PCAP_PCAP_SSS_MASK 0x5U +#define PCAP_PL_DONE_MASK 0x8U +#define PCAP_CFG_RESET 0x40U +#define CSUDMA_I_STS_DONE_MASK 0x2U +#define CSUDMA_SRC_ADDR_MASK 0xFFFFFFFCU +#define CSUDMA_SRC_SIZE_SHIFT 0x2U + +#define IDCODE_MASK 0xFFFFFFF +#define ZU2_IDCODE 0x4711093 +#define ZU3_IDCODE 0x4710093 +#define ZU4_IDCODE 0x4721093 +#define ZU5_IDCODE 0x4720093 +#define ZU6_IDCODE 0x4739093 +#define ZU7_IDCODE 0x4730093 +#define ZU9_IDCODE 0x4738093 +#define ZU11_IDCODE 0x4740093 +#define ZU15_IDCODE 0x4750093 +#define ZU17_IDCODE 0x4759093 +#define ZU19_IDCODE 0x4758093 +#define ZU21_IDCODE 0x47E1093 +#define ZU25_IDCODE 0x47E5093 +#define ZU27_IDCODE 0x47E4093 +#define ZU28_IDCODE 0x47E0093 +#define ZU29_IDCODE 0x47E2093 +#define ZU39_IDCODE 0x47E6093 +#define ZU43_IDCODE 0x47FD093 +#define ZU46_IDCODE 0x47F8093 +#define ZU47_IDCODE 0x47FF093 +#define ZU48_IDCODE 0x47FB093 +#define ZU49_IDCODE 0x47FE093 + +#define XLNX_BITSTREAM_SECTION_LENGTH(data) (*(data + 1) | *data << 0x8U); + +#endif /* ZEPHYR_DRIVERS_FPGA_ZYNQMP_H */ diff --git a/dts/bindings/fpga/xlnx,fpga.yaml b/dts/bindings/fpga/xlnx,fpga.yaml new file mode 100644 index 00000000000..9a0c0eee1e4 --- /dev/null +++ b/dts/bindings/fpga/xlnx,fpga.yaml @@ -0,0 +1,6 @@ +# Copyright (c) 2022 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +description: Zynqmp FPGA driver + +compatible: "xlnx,fpga"