diff --git a/drivers/fpga/CMakeLists.txt b/drivers/fpga/CMakeLists.txt index 3db822ad0d0..e7a16193c4c 100644 --- a/drivers/fpga/CMakeLists.txt +++ b/drivers/fpga/CMakeLists.txt @@ -6,4 +6,5 @@ zephyr_library_sources_ifdef(CONFIG_FPGA_SHELL fpga_shell.c) zephyr_library_sources_ifdef(CONFIG_EOS_S3_FPGA fpga_eos_s3.c) zephyr_library_sources_ifdef(CONFIG_ICE40_FPGA fpga_ice40.c) +zephyr_library_sources_ifdef(CONFIG_MPFS_FPGA fpga_mpfs.c) zephyr_library_sources_ifdef(CONFIG_ZYNQMP_FPGA fpga_zynqmp.c) diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index a9573137be1..6041a164f18 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -28,6 +28,7 @@ config FPGA_SHELL source "drivers/fpga/Kconfig.eos_s3" source "drivers/fpga/Kconfig.ice40" +source "drivers/fpga/Kconfig.mpfs" source "drivers/fpga/Kconfig.zynqmp" endif # FPGA diff --git a/drivers/fpga/Kconfig.mpfs b/drivers/fpga/Kconfig.mpfs new file mode 100644 index 00000000000..197921015ed --- /dev/null +++ b/drivers/fpga/Kconfig.mpfs @@ -0,0 +1,10 @@ +# FPGA Microchip PolarFire SOC driver configuration options + +# Copyright (c) 2023 Microchip Technology Inc. +# SPDX-License-Identifier: Apache-2.0 + +config MPFS_FPGA + bool "Microchip PolarFire SOC FPGA driver" + depends on SPI + help + Enable support for the Microchip PolarFire SOC FPGA driver. diff --git a/drivers/fpga/fpga_mpfs.c b/drivers/fpga/fpga_mpfs.c new file mode 100644 index 00000000000..d8c777a4501 --- /dev/null +++ b/drivers/fpga/fpga_mpfs.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2024 Microchip Technology Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_mpfs_mailbox +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(fpga_mpfs); + +#define SPI_FLASH_DIRECTORY_OFFSET 0x00000000 +#define SPI_FLASH_GOLDEN_IMAGE_OFFSET 0x00100400 +#define SPI_FLASH_NEW_IMAGE_OFFSET 0x01500400 +#define SPI_FLASH_SECTOR_SIZE 4096 +#define SPI_FLASH_PAGE_SIZE 256 + +#define SERVICES_CR_OFFSET 0x50u +#define SERVICES_SR_OFFSET 0x54u + +#define SCBCTRL_SERVICESCR_REQ (0u) +#define SCBCTRL_SERVICESCR_REQ_MASK BIT(SCBCTRL_SERVICESCR_REQ) + +#define SCBCTRL_SERVICESSR_BUSY (1u) +#define SCBCTRL_SERVICESSR_BUSY_MASK BIT(SCBCTRL_SERVICESSR_BUSY) + +#define SCBCTRL_SERVICESSR_STATUS (16u) +#define SCBCTRL_SERVICESSR_STATUS_MASK_WIDTH (16u) +#define SCBCTRL_SERVICESSR_STATUS_MASK \ + GENMASK(SCBCTRL_SERVICESSR_STATUS + SCBCTRL_SERVICESSR_STATUS_MASK_WIDTH - 1, \ + SCBCTRL_SERVICESSR_STATUS) + +#define MSS_DESIGN_INFO_CMD (0x02) +#define MSS_SYS_BITSTREAM_AUTHENTICATE_CMD 0x23u +#define MSS_SYS_IAP_PROGRAM_BY_SPIIDX_CMD 0x42u + +struct mpfs_fpga_config { + mm_reg_t base; + mm_reg_t mailbox; +}; + +struct mpfs_fpga_data { + char FPGA_design_ver[30]; +}; + +static inline uint32_t scb_read(mm_reg_t add, mm_reg_t offset) +{ + return sys_read32(add + offset); +} + +static inline void scb_write(mm_reg_t add, mm_reg_t offset, uint32_t val) +{ + return sys_write32(val, add + offset); +} + +/*This function add the index of new image into the spi directory at offset 0x004. + * Note: In the Flash directory first four pages(each page of 256 Bytes) have either + * a valid image address or zeros. The other remaining 12 pages are all filled with 0xFFs. + * + * |------------------------------| 0x000 + * | Golden Image Address: | + * | 0x0100400 | + * |------------------------------| 0x004 + * | Update Image Address | + * | 0x1500400 | + * |------------------------------| 0x008 + * | Empty | + * | 0x000000 | + * |------------------------------| 0x00C + * | Unused for re-programming | + * | | + * |------------------------------| 0x400 + */ +static uint8_t update_spi_flash_directory(const struct device *flash_dev) +{ + size_t len = SPI_FLASH_PAGE_SIZE; + uint8_t buf[SPI_FLASH_PAGE_SIZE]; + uint8_t rc, k; + + memset(buf, 0, len); + + rc = flash_read(flash_dev, SPI_FLASH_DIRECTORY_OFFSET, buf, len); + if (rc != 0) { + LOG_ERR("Flash read failed! %d", rc); + return rc; + } + + /* New image address(0x1500400) entry at offset 0x004 */ + buf[4] = 0x00; + buf[5] = 0x04; + buf[6] = 0x50; + buf[7] = 0x01; + + /* Erase SPI flash directory */ + + rc = flash_erase(flash_dev, SPI_FLASH_DIRECTORY_OFFSET, SPI_FLASH_SECTOR_SIZE); + if (rc != 0) { + LOG_ERR("erase failed! %d", rc); + } + + /* Write the first page with updated address entry */ + rc = flash_write(flash_dev, SPI_FLASH_DIRECTORY_OFFSET, buf, len); + if (rc != 0) { + LOG_ERR("Flash write failed! %d", rc); + return rc; + } + + /* Fill page number second, third and fourth with zeros */ + memset(buf, 0, len); + k = 1; + while (k < 4) { + rc = flash_write(flash_dev, (SPI_FLASH_DIRECTORY_OFFSET + k * 0x100), buf, len); + if (rc != 0) { + LOG_ERR("Flash write failed! %d", rc); + return rc; + } + k++; + } + + return rc; +} + +/* This function Program a new FPGA design image into the SPI Flash at location + * 0x1500400. + * Note: The source location of new image is _bin_start symbol value and the size of + * new image is _bim_size symbol value. + */ +static uint8_t program_new_image(const struct device *flash_dev, uint8_t *image_start, + uint32_t image_size) +{ + size_t len = SPI_FLASH_PAGE_SIZE; + uint8_t buf[SPI_FLASH_PAGE_SIZE]; + uint8_t rc; + uint32_t i, count, k; + uint8_t *temp; + + temp = image_start; + + if (image_size > 0x1400000) { + LOG_ERR("Image is larger than 20Mb"); + return 1; + } + + /* Find the sectors to erase */ + count = (uint32_t)(image_size / SPI_FLASH_SECTOR_SIZE) + 1; + + LOG_INF("Erasing."); + i = 0; + while (i < count) { + rc = flash_erase( + flash_dev, + ((SPI_FLASH_NEW_IMAGE_OFFSET - 0x400) + (i * SPI_FLASH_SECTOR_SIZE)), + SPI_FLASH_SECTOR_SIZE); + if (rc != 0) { + LOG_ERR("erase failed! %d", rc); + } + + if (i % 0x100 == 0) { + LOG_DBG("."); + } + + i++; + } + /* Erase completed and ready to program new image */ + + /* Find the pages to program */ + count = (uint32_t)(image_size / SPI_FLASH_PAGE_SIZE) + 1; + + LOG_INF("Programming."); + i = 0; + while (i < count) { + temp = (image_start + i * SPI_FLASH_PAGE_SIZE); + memset(buf, 0, len); + for (k = 0; k < 256; k++) { + buf[k] = *temp; + temp = temp + 1; + } + + rc = flash_write(flash_dev, (SPI_FLASH_NEW_IMAGE_OFFSET + i * SPI_FLASH_PAGE_SIZE), + buf, len); + if (rc != 0) { + LOG_ERR("Flash write failed! %d", rc); + return rc; + } + + if (i % 0x100 == 0) { + LOG_DBG("."); + } + + i++; + } + + LOG_INF("Programming completed."); + + return rc; +} + +static int8_t verify_image(const struct device *dev) +{ + const struct mpfs_fpga_config *cfg = dev->config; + int8_t status = EINVAL; + uint32_t value = 0; + + LOG_INF("Image verification started..."); + + /* Once system controller starts processing command The busy bit will + * go 1. Make sure that service is complete i.e. BUSY bit is gone 0 + */ + while (scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_BUSY_MASK) { + ; + } + + /* Form the SS command: bit 0 to 6 is the opcode, bit 7 to 15 is the Mailbox + * offset For some services this field has another meaning. + * (e.g. for IAP bit-stream auth. it means spi_idx) + */ + scb_write(cfg->mailbox, 0, 0x1500400); + + value = (MSS_SYS_BITSTREAM_AUTHENTICATE_CMD << 16) | 0x1; + scb_write(cfg->base, SERVICES_CR_OFFSET, value); + + /* REQ bit will remain set till the system controller starts + * processing command. Since DRI is slow interface, we are waiting + * here to make sure System controller has started processing + * command + */ + while (scb_read(cfg->base, SERVICES_CR_OFFSET) & SCBCTRL_SERVICESCR_REQ_MASK) { + ; + } + + /* Once system controller starts processing command The busy bit will + * go 1. Make sure that service is complete i.e. BUSY bit is gone 0 + */ + while (scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_BUSY_MASK) { + ; + } + + /* Read the status returned by System Controller */ + status = ((scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_STATUS_MASK) >> + SCBCTRL_SERVICESSR_STATUS); + LOG_INF("Image verification status : %x ", status); + + return status; +} + +static void activate_image(const struct device *dev) +{ + const struct mpfs_fpga_config *cfg = dev->config; + int8_t status = EINVAL; + uint32_t value = 0; + + LOG_INF("Image activation started..."); + + /* Once system controller starts processing command The busy bit will + * go 1. Make sure that service is complete i.e. BUSY bit is gone 0 + */ + while (scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_BUSY_MASK) { + ; + } + + /* Form the SS command: bit 0 to 6 is the opcode, bit 7 to 15 is the Mailbox + * offset For some services this field has another meaning. + * (e.g. for IAP bit-stream auth. it means spi_idx) + */ + value = (MSS_SYS_IAP_PROGRAM_BY_SPIIDX_CMD << 16) | BIT(23) | 0x1; + scb_write(cfg->base, SERVICES_CR_OFFSET, value); + + /* REQ bit will remain set till the system controller starts + * processing command. Since DRI is slow interface, we are waiting + * here to make sure System controller has started processing + * command + */ + while (scb_read(cfg->base, SERVICES_CR_OFFSET) & SCBCTRL_SERVICESCR_REQ_MASK) { + ; + } + + /* Once system controller starts processing command The busy bit will + * go 1. Make sure that service is complete i.e. BUSY bit is gone 0 + */ + while (scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_BUSY_MASK) { + ; + } + + /* Read the status returned by System Controller */ + status = ((scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_STATUS_MASK) >> + SCBCTRL_SERVICESSR_STATUS); + LOG_INF("Image activation status : %x ", status); +} + +static int mpfs_fpga_reset(const struct device *dev) +{ + int8_t status = EINVAL; + + status = verify_image(dev); + if (status == 0) { + activate_image(dev); + } + return 0; +} + +static int mpfs_fpga_load(const struct device *dev, uint32_t *image_ptr, uint32_t img_size) +{ + const struct device *flash_dev = DEVICE_DT_GET_OR_NULL(DT_ALIAS(bitstream_flash)); + + if (flash_dev == NULL) { + LOG_ERR("Device not found"); + return -ENOENT; + } + + if (!device_is_ready(flash_dev)) { + LOG_ERR("%s: device not ready.", flash_dev->name); + return 1; + } + + if (img_size == 0) { + LOG_ERR("Image size is zero."); + return -EINVAL; + } + + if (image_ptr == NULL) { + LOG_ERR("Failed to read FPGA image"); + return -EINVAL; + } + + update_spi_flash_directory(flash_dev); + program_new_image(flash_dev, (uint8_t *)image_ptr, img_size); + return 0; +} + +static const char *mpfs_fpga_get_info(const struct device *dev) +{ + struct mpfs_fpga_data *data = dev->data; + const struct mpfs_fpga_config *cfg = dev->config; + uint32_t value = 0; + uint16_t design_version = 0; + + /* Once system controller starts processing command The busy bit will + * go 1. Make sure that service is complete i.e. BUSY bit is gone 0 + */ + while (scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_BUSY_MASK) { + ; + } + + /* Form the SS command: bit 0 to 6 is the opcode, bit 7 to 15 is the Mailbox + * offset For some services this field has another meaning. + * (e.g. for IAP bit-stream auth. it means spi_idx) + */ + + value = (MSS_DESIGN_INFO_CMD << 16) | 0x1; + scb_write(cfg->base, SERVICES_CR_OFFSET, value); + + /* REQ bit will remain set till the system controller starts + * processing command. Since DRI is slow interface, we are waiting + * here to make sure System controller has started processing + * command + */ + while (scb_read(cfg->base, SERVICES_CR_OFFSET) & SCBCTRL_SERVICESCR_REQ_MASK) { + ; + } + + /* Once system controller starts processing command The busy bit will + * go 1. Make sure that service is complete i.e. BUSY bit is gone 0 + */ + while (scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_BUSY_MASK) { + ; + } + + design_version = scb_read(cfg->mailbox, 32); + sprintf(data->FPGA_design_ver, (uint8_t *)"Design Version : 0x%x", design_version); + + return data->FPGA_design_ver; +} + +static enum FPGA_status mpfs_fpga_get_status(const struct device *dev) +{ + const struct mpfs_fpga_config *cfg = dev->config; + + if (scb_read(cfg->base, SERVICES_SR_OFFSET) & SCBCTRL_SERVICESSR_BUSY_MASK) { + return FPGA_STATUS_INACTIVE; + } else { + return FPGA_STATUS_ACTIVE; + } +} + +static int mpfs_fpga_init(const struct device *dev) +{ + return 0; +} + +static struct mpfs_fpga_data fpga_data; + +static struct mpfs_fpga_config fpga_config = { + .base = DT_INST_REG_ADDR_BY_IDX(0, 0), + .mailbox = DT_INST_REG_ADDR_BY_IDX(0, 2), +}; + +static const struct fpga_driver_api mpfs_fpga_api = { + .reset = mpfs_fpga_reset, + .load = mpfs_fpga_load, + .get_info = mpfs_fpga_get_info, + .get_status = mpfs_fpga_get_status, +}; + +DEVICE_DT_INST_DEFINE(0, &mpfs_fpga_init, NULL, &fpga_data, &fpga_config, POST_KERNEL, + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mpfs_fpga_api);