common/util/util_spi.c (220 lines of code) (raw):
#include <zephyr.h>
#include <sys/printk.h>
#include <logging/log.h>
#include <shell/shell.h>
#include <drivers/flash.h>
#include <device.h>
#include <soc.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/util.h>
#include "cmsis_os2.h"
#include "util_spi.h"
#include "util_sys.h"
static char *flash_device[6] = { "fmc_cs0", "fmc_cs1", "spi1_cs0",
"spi1_cs1", "spi2_cs0", "spi2_cs1" };
static int do_erase_write_verify(const struct device *flash_device, uint32_t op_addr,
uint8_t *write_buf, uint8_t *read_back_buf, uint32_t erase_sz)
{
uint32_t ret = 0;
uint32_t i;
ret = flash_erase(flash_device, op_addr, erase_sz);
if (ret != 0) {
printk("[%s][%d] erase failed at %d.\n", __func__, __LINE__, op_addr);
goto end;
}
ret = flash_write(flash_device, op_addr, write_buf, erase_sz);
if (ret != 0) {
printk("[%s][%d] write failed at %d.\n", __func__, __LINE__, op_addr);
goto end;
}
ret = flash_read(flash_device, op_addr, read_back_buf, erase_sz);
if (ret != 0) {
printk("[%s][%d] write failed at %d.\n", __func__, __LINE__, op_addr);
goto end;
}
if (memcmp(write_buf, read_back_buf, erase_sz) != 0) {
ret = -EINVAL;
printk("ERROR: %s %d fail to write flash at 0x%x\n", __func__, __LINE__, op_addr);
printk("to be written:\n");
for (i = 0; i < 256; i++) {
printk("%x ", write_buf[i]);
if (i % 16 == 15)
printk("\n");
}
printk("readback:\n");
for (i = 0; i < 256; i++) {
printk("%x ", read_back_buf[i]);
if (i % 16 == 15)
printk("\n");
}
goto end;
}
end:
return ret;
}
static int do_update(const struct device *flash_device, off_t offset, uint8_t *buf, size_t len)
{
int ret = 0;
uint32_t flash_sz = flash_get_flash_size(flash_device);
uint32_t sector_sz = flash_get_write_block_size(flash_device);
uint32_t flash_offset = (uint32_t)offset;
uint32_t remain, op_addr = 0, end_sector_addr;
uint8_t *update_ptr = buf, *op_buf = NULL, *read_back_buf = NULL;
bool update_it = false;
if (flash_sz < flash_offset + len) {
printk("ERROR: update boundary exceeds flash size. (%d, %d, %u)\n", flash_sz,
flash_offset, len);
ret = -EINVAL;
goto end;
}
op_buf = (uint8_t *)malloc(sector_sz);
if (op_buf == NULL) {
printk("heap full %d %d\n", __LINE__, sector_sz);
ret = -EINVAL;
goto end;
}
read_back_buf = (uint8_t *)malloc(sector_sz);
if (read_back_buf == NULL) {
printk("heap full %d %d\n", __LINE__, sector_sz);
ret = -EINVAL;
goto end;
}
/* initial op_addr */
op_addr = (flash_offset / sector_sz) * sector_sz;
/* handle the start part which is not multiple of sector size */
if (flash_offset % sector_sz != 0) {
ret = flash_read(flash_device, op_addr, op_buf, sector_sz);
if (ret != 0)
goto end;
remain = MIN(sector_sz - (flash_offset % sector_sz), len);
memcpy((uint8_t *)op_buf + (flash_offset % sector_sz), update_ptr, remain);
ret = do_erase_write_verify(flash_device, op_addr, op_buf, read_back_buf,
sector_sz);
if (ret != 0)
goto end;
op_addr += sector_sz;
update_ptr += remain;
}
end_sector_addr = (flash_offset + len) / sector_sz * sector_sz;
/* handle body */
for (; op_addr < end_sector_addr;) {
ret = flash_read(flash_device, op_addr, op_buf, sector_sz);
if (ret != 0)
goto end;
if (memcmp(op_buf, update_ptr, sector_sz) != 0)
update_it = true;
if (update_it) {
ret = do_erase_write_verify(flash_device, op_addr, update_ptr,
read_back_buf, sector_sz);
if (ret != 0)
goto end;
}
op_addr += sector_sz;
update_ptr += sector_sz;
}
/* handle remain part */
if (end_sector_addr < flash_offset + len) {
ret = flash_read(flash_device, op_addr, op_buf, sector_sz);
if (ret != 0)
goto end;
remain = flash_offset + len - end_sector_addr;
memcpy((uint8_t *)op_buf, update_ptr, remain);
ret = do_erase_write_verify(flash_device, op_addr, op_buf, read_back_buf,
sector_sz);
if (ret != 0)
goto end;
op_addr += remain;
}
end:
if (op_buf != NULL)
free(op_buf);
if (read_back_buf != NULL)
free(read_back_buf);
return ret;
}
uint8_t fw_update(uint32_t offset, uint16_t msg_len, uint8_t *msg_buf, bool update_en,
uint8_t spi_bus)
{
static bool is_init = 0;
static uint8_t *txbuf = NULL;
static uint32_t buf_offset = 0;
uint32_t ret = 0;
const struct device *flash_dev;
if (!is_init) {
if ((offset & 0x0000) != 0) {
return fwupdate_error_offset;
}
if (txbuf != NULL) {
free(txbuf);
txbuf = NULL;
}
txbuf = (uint8_t *)malloc(sector_sz_64k);
if (txbuf == NULL) { // Retry alloc
k_msleep(100);
txbuf = (uint8_t *)malloc(sector_sz_64k);
}
if (txbuf == NULL) {
printk("spi index%x update buffer alloc fail\n", spi_bus);
return fwupdate_out_of_heap;
}
is_init = 1;
buf_offset = 0;
k_msleep(10);
}
if ((buf_offset + msg_len) > sector_sz_64k) {
printk("spi bus%x recv data %d over sector size %d\n", spi_bus,
buf_offset + msg_len, sector_sz_64k);
free(txbuf);
txbuf = NULL;
k_msleep(10);
is_init = 0;
return fwupdate_over_length;
}
if ((offset % sector_sz_64k) != buf_offset) {
printk("spi bus%x recorded offset 0x%x but updating 0x%x\n", spi_bus, buf_offset,
offset % sector_sz_64k);
free(txbuf);
txbuf = NULL;
k_msleep(10);
is_init = 0;
return fwupdate_repeated_updated;
}
if (FW_UPDATE_DEBUG) {
printk("spi bus%x update offset %x %x, msg_len %d, update_en %d, msg_buf: %2x %2x %2x %2x\n",
spi_bus, offset, buf_offset, msg_len, update_en, msg_buf[0], msg_buf[1],
msg_buf[2], msg_buf[3]);
}
memcpy(&txbuf[buf_offset], msg_buf, msg_len);
buf_offset += msg_len;
if ((buf_offset == sector_sz_64k) ||
update_en) { // Update fmc while collect 64k bytes data or BMC signal last image package with target | 0x80
flash_dev = device_get_binding(flash_device[spi_bus]);
uint8_t sector = 16;
uint32_t txbuf_offset;
uint32_t update_offset;
for (int i = 0; i < sector; i++) {
txbuf_offset = sector_sz_4k * i;
update_offset = (offset / sector_sz_64k) * sector_sz_64k + txbuf_offset;
ret = do_update(flash_dev, update_offset, &txbuf[txbuf_offset],
sector_sz_4k);
if (ret) {
printk("SPI update fail status: %x\n", ret);
break;
}
}
if (!ret) {
printk("Update success\n");
}
free(txbuf);
txbuf = NULL;
k_msleep(10);
is_init = 0;
if (FW_UPDATE_DEBUG) {
printk("***update %x, offset %x, sector_sz_16k %x\n",
(offset / sector_sz_16k) * sector_sz_16k, offset, sector_sz_16k);
}
if (update_en && spi_bus == (devspi_fmc_cs0)) { // reboot bic itself after fw update
submit_bic_warm_reset();
}
return ret;
}
return fwupdate_success;
}