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.
413 lines
11 KiB
413 lines
11 KiB
/* |
|
* Copyright (c) 2024 Microchip Technology Inc. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT microchip_mpfs_mailbox |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <zephyr/device.h> |
|
#include <zephyr/devicetree.h> |
|
#include <zephyr/drivers/flash.h> |
|
#include <zephyr/drivers/fpga.h> |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/logging/log.h> |
|
#include <zephyr/sys/sys_io.h> |
|
#include <zephyr/sys/util.h> |
|
LOG_MODULE_REGISTER(fpga_mpfs, CONFIG_FPGA_LOG_LEVEL); |
|
|
|
#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) |
|
{ |
|
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 DEVICE_API(fpga, 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);
|
|
|