|  | /* | 
|  | * Copyright (c) 2021-2022 Antmicro <www.antmicro.com> | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #define DT_DRV_COMPAT xlnx_fpga | 
|  |  | 
|  | #include <zephyr/device.h> | 
|  | #include <zephyr/drivers/fpga.h> | 
|  | #include "fpga_zynqmp.h" | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  | #include <zephyr/sys/byteorder.h> | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include <zephyr/logging/log.h> | 
|  | 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, | 
|  | POST_KERNEL, CONFIG_FPGA_INIT_PRIORITY, &zynqmp_api); |