fpga controller: drivers: add ZynqMP driver
This commit adds support for fpga driver on ZynqMP SoC. Signed-off-by: Mateusz Sierszulski <msierszulski@antmicro.com>
This commit is contained in:
parent
314a3a6ef4
commit
06e4f36b4b
|
@ -19,6 +19,7 @@
|
|||
|
||||
fpga0: fpga {
|
||||
status = "okay";
|
||||
compatible = "xlnx,fpga";
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# FPGA driver configuration options
|
||||
|
||||
# Copyright (c) 2021 Antmicro <www.antmicro.com>
|
||||
# Copyright (c) 2021-2022 Antmicro <www.antmicro.com>
|
||||
# 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
|
||||
|
|
9
drivers/fpga/Kconfig.zynqmp
Normal file
9
drivers/fpga/Kconfig.zynqmp
Normal file
|
@ -0,0 +1,9 @@
|
|||
# FPGA ZYNQMP driver configuration options
|
||||
|
||||
# Copyright (c) 2021-2022 Antmicro <www.antmicro.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config ZYNQMP_FPGA
|
||||
bool "ZYNQMP fpga driver"
|
||||
help
|
||||
Enable ZYNQMP FPGA driver.
|
326
drivers/fpga/fpga_zynqmp.c
Normal file
326
drivers/fpga/fpga_zynqmp.c
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT xlnx_fpga
|
||||
|
||||
#include <device.h>
|
||||
#include <drivers/fpga.h>
|
||||
#include "fpga_zynqmp.h"
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/byteorder.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <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,
|
||||
APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &zynqmp_api);
|
71
drivers/fpga/fpga_zynqmp.h
Normal file
71
drivers/fpga/fpga_zynqmp.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022 Antmicro <www.antmicro.com>
|
||||
*
|
||||
* 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 */
|
6
dts/bindings/fpga/xlnx,fpga.yaml
Normal file
6
dts/bindings/fpga/xlnx,fpga.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Copyright (c) 2022 Antmicro <www.antmicro.com>
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Zynqmp FPGA driver
|
||||
|
||||
compatible: "xlnx,fpga"
|
Loading…
Reference in a new issue