You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
6.4 KiB
326 lines
6.4 KiB
/* |
|
* 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, CONFIG_FPGA_LOG_LEVEL); |
|
|
|
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 DEVICE_API(fpga, 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);
|
|
|