/* * 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);