mirror of
https://github.com/drasko/open-ameba.git
synced 2024-12-28 00:55:19 +00:00
1075 lines
34 KiB
C
1075 lines
34 KiB
C
/********************************************************************************
|
|
* Copyright (c) 2014, Realtek Semiconductor Corp.
|
|
* All rights reserved.
|
|
*
|
|
* This module is a confidential and proprietary property of RealTek and
|
|
* possession or use of this module requires written permission of RealTek.
|
|
*******************************************************************************
|
|
*/
|
|
|
|
#include "xmport_uart.h"
|
|
#include "xmport_loguart.h"
|
|
#include "rtl8195a.h"
|
|
#include "xmodem.h"
|
|
#include "xmport_uart.h"
|
|
#include "hal_spi_flash.h"
|
|
#include "rtl8195a_spi_flash.h"
|
|
#include <platform/platform_stdlib.h>
|
|
|
|
#if CONFIG_UART_SOCKET
|
|
#if /*CONFIG_PERI_UPDATE_IMG*/1
|
|
|
|
#define IMG1_SIGN_OFFSET 0x34
|
|
|
|
enum {
|
|
XMODEM_UART_0 = 0,
|
|
XMODEM_UART_1 = 1,
|
|
XMODEM_UART_2 = 2,
|
|
XMODEM_LOG_UART = 3
|
|
};
|
|
|
|
FWU_DATA_SECTION char xMFrameBuf[XM_BUFFER_SIZE];
|
|
FWU_DATA_SECTION XMODEM_CTRL xMCtrl;
|
|
|
|
FWU_DATA_SECTION static u32 fw_img1_size;
|
|
FWU_DATA_SECTION static u32 fw_img2_size;
|
|
FWU_DATA_SECTION static u32 fw_img2_addr;
|
|
FWU_DATA_SECTION static u32 fw_img3_size;
|
|
FWU_DATA_SECTION static u32 fw_img3_addr;
|
|
FWU_DATA_SECTION static u32 flash_wr_offset;
|
|
FWU_DATA_SECTION static u32 flash_erased_addr;
|
|
FWU_DATA_SECTION static u8 start_with_img1;
|
|
FWU_DATA_SECTION static u32 flash_wr_err_cnt;
|
|
|
|
FWU_DATA_SECTION HAL_RUART_ADAPTER xmodem_uart_adp; // we can dynamic allocate memory for this object to save memory
|
|
|
|
FWU_RODATA_SECTION const char Img2Signature[8]="81958711";
|
|
extern u32 SpicCalibrationPattern[4];
|
|
extern const u8 ROM_IMG1_VALID_PATTEN[];
|
|
extern HAL_RUART_ADAPTER *pxmodem_uart_adp;
|
|
|
|
#ifdef CONFIG_GPIO_EN
|
|
extern HAL_GPIO_ADAPTER gBoot_Gpio_Adapter;
|
|
extern PHAL_GPIO_ADAPTER _pHAL_Gpio_Adapter;
|
|
#endif
|
|
|
|
extern BOOLEAN SpicFlashInitRtl8195A(u8 SpicBitMode);
|
|
_LONG_CALL_
|
|
extern VOID SpicWaitBusyDoneRtl8195A(VOID);
|
|
extern VOID SpicWaitWipDoneRefinedRtl8195A(SPIC_INIT_PARA SpicInitPara);
|
|
|
|
VOID WriteImg1Sign(u32 Image2Addr);
|
|
|
|
FWU_TEXT_SECTION void FWU_WriteWord(u32 Addr, u32 FData)
|
|
{
|
|
SPIC_INIT_PARA SpicInitPara;
|
|
|
|
HAL_WRITE32(SPI_FLASH_BASE, Addr, FData);
|
|
// Wait spic busy done
|
|
SpicWaitBusyDoneRtl8195A();
|
|
// Wait flash busy done (wip=0)
|
|
SpicWaitWipDoneRefinedRtl8195A(SpicInitPara);
|
|
}
|
|
|
|
FWU_TEXT_SECTION u32 xModem_MemCmp(const u32 *av, const u32 *bv, u32 len)
|
|
{
|
|
const u32 *a = av;
|
|
const u32 *b = (u32*)((u8*)bv+SPI_FLASH_BASE);
|
|
u32 len4b = len >> 2;
|
|
u32 i;
|
|
|
|
for (i=0; i<len4b; i++) {
|
|
if (a[i] != b[i]) {
|
|
DBG_MISC_ERR("OTU: Flash write check error @ 0x%08x\r\n", (u32)(&b[i]));
|
|
return ((u32)(&b[i]));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u32 xModem_Frame_Dump(char *ptr, unsigned int frame_num)
|
|
{
|
|
u32 i;
|
|
|
|
DiagPrintf("===== Frme %d ======\r\n", frame_num);
|
|
|
|
for(i=0;i<128;i+=16) {
|
|
DiagPrintf("%02x: ", i);
|
|
DiagPrintf("%02x %02x %02x %02x %02x %02x %02x %02x ",
|
|
*(ptr+i),*(ptr+i+1),*(ptr+i+2),*(ptr+i+3),*(ptr+i+4),*(ptr+i+5),*(ptr+i+6),*(ptr+i+7));
|
|
DiagPrintf("%02x %02x %02x %02x %02x %02x %02x %02x \r\n",
|
|
*(ptr+i+8),*(ptr+i+9),*(ptr+i+10),*(ptr+i+11),*(ptr+i+12),*(ptr+i+13),*(ptr+i+14),*(ptr+i+15));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u32 xModem_Frame_MemWrite(char *ptr, unsigned int frame_num, unsigned int frame_size)
|
|
{
|
|
u32 idx=0;
|
|
u32 skip_sz=0;
|
|
|
|
// "flash_wr_offset" here is used as the skip bytes from the head
|
|
while ((flash_wr_offset > 0) && (idx < frame_size)) {
|
|
flash_wr_offset--;
|
|
idx++;
|
|
skip_sz++;
|
|
}
|
|
|
|
// "fw_img2_size" here is used as the memory length to write
|
|
// "fw_img2_addr" is used as the start memory address to write
|
|
while (idx < frame_size) {
|
|
if (fw_img2_size == 0) {
|
|
return idx;
|
|
}
|
|
|
|
if (((fw_img2_addr & 0x03) == 0) &&
|
|
(fw_img2_size > 3) &&
|
|
((frame_size - idx) > 3)) {
|
|
// write address is 4-byte aligned
|
|
*((u32*)fw_img2_addr) = (*((u32*)(ptr+idx)));
|
|
fw_img2_addr += 4;
|
|
fw_img2_size -= 4;
|
|
idx += 4;
|
|
} else if (fw_img2_size > 0){
|
|
*((u8*)fw_img2_addr) = (*((u8*)(ptr+idx)));
|
|
fw_img2_addr++;
|
|
fw_img2_size--;
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
return (idx - skip_sz);
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u32 xModem_Frame_FlashWrite(char *ptr, unsigned int frame_num, unsigned int frame_size)
|
|
{
|
|
u32 idx=0;
|
|
u32 skip_sz=0;
|
|
u32 temp;
|
|
u32 i;
|
|
|
|
// "flash_wr_offset" here is used as the skip bytes from the head
|
|
while ((flash_wr_offset > 0) && (idx < frame_size)) {
|
|
flash_wr_offset--;
|
|
idx++;
|
|
skip_sz++;
|
|
}
|
|
|
|
// "fw_img2_size" here is used as the memory length to write
|
|
// "fw_img2_addr" is used as the start memory address to write
|
|
while (idx < frame_size) {
|
|
if (fw_img2_size == 0) {
|
|
return idx;
|
|
}
|
|
|
|
if ((fw_img2_size > 3) && ((frame_size - idx) > 3)) {
|
|
FWU_WriteWord(fw_img2_addr, (*((u32*)(ptr+idx))));
|
|
fw_img2_addr += 4;
|
|
fw_img2_size -= 4;
|
|
idx += 4;
|
|
} else {
|
|
temp = 0xFFFFFFFF;
|
|
for (i=0;i<4;i++) {
|
|
// Just for little endian
|
|
*((((u8*)&temp) + i)) = (*((u8*)(ptr+idx)));
|
|
idx++;
|
|
fw_img2_size--;
|
|
if ((fw_img2_size == 0) || (idx >= frame_size)) {
|
|
break;
|
|
}
|
|
}
|
|
FWU_WriteWord(fw_img2_addr, temp);
|
|
fw_img2_addr += 4;
|
|
}
|
|
}
|
|
|
|
return (idx - skip_sz);
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u32 xModem_Frame_Img2(char *ptr, unsigned int frame_num, unsigned int frame_size)
|
|
{
|
|
u32 address;
|
|
u32 ImageIndex=0;
|
|
u32 rx_len=0;
|
|
u32 *chk_sr;
|
|
u32 *chk_dr;
|
|
u32 err_addr;
|
|
|
|
if (frame_num == 1) {
|
|
// Parse Image2 header
|
|
flash_wr_offset = fw_img2_addr;
|
|
fw_img2_size = rtk_le32_to_cpu(*((u32*)ptr)) + 0x10;
|
|
if ((fw_img2_size & 0x03) != 0) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Err#2: fw_img2_addr=0x%x fw_img2_size(%d) isn't 4-bytes aligned\r\n", fw_img2_addr, fw_img2_size);
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
if (fw_img2_size > (2*1024*1024)) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Image2 to Big: fw_img2_addr=0x%x fw_img2_size(%d) \r\n", fw_img2_addr, fw_img2_size);
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
return rx_len;
|
|
}
|
|
fw_img3_addr = fw_img2_addr + fw_img2_size;
|
|
|
|
// erase Flash first
|
|
address = fw_img2_addr & (~0xfff); // 4k aligned, 4k is the page size of flash memory
|
|
while ((address) < (fw_img2_addr+fw_img2_size)) {
|
|
SpicSectorEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x1000;
|
|
}
|
|
flash_erased_addr = address;
|
|
}
|
|
|
|
if (fw_img2_size > 0) {
|
|
// writing image2
|
|
chk_sr = (u32*)((u8*)ptr+ImageIndex);
|
|
chk_dr = (u32*)flash_wr_offset;
|
|
while (ImageIndex < frame_size) {
|
|
FWU_WriteWord(flash_wr_offset, (*((u32*)(ptr+ImageIndex))));
|
|
ImageIndex += 4;
|
|
flash_wr_offset += 4;
|
|
rx_len += 4;
|
|
fw_img2_size -= 4;
|
|
if (fw_img2_size == 0) {
|
|
// Image2 write done,
|
|
break;
|
|
}
|
|
}
|
|
|
|
err_addr = xModem_MemCmp(chk_sr, chk_dr, (flash_wr_offset - (u32)chk_dr));
|
|
if (err_addr) {
|
|
flash_wr_err_cnt++;
|
|
}
|
|
}
|
|
|
|
if (ImageIndex >= frame_size) {
|
|
return rx_len;
|
|
}
|
|
|
|
// Skip the gap between image2 and image3,
|
|
// there is no gap in current image format
|
|
if (flash_wr_offset < fw_img3_addr) {
|
|
if ((flash_wr_offset + (frame_size-ImageIndex)) <= fw_img3_addr) {
|
|
flash_wr_offset += (frame_size-ImageIndex);
|
|
return rx_len;
|
|
} else {
|
|
while (ImageIndex < frame_size) {
|
|
if (flash_wr_offset == fw_img3_addr) {
|
|
break;
|
|
}
|
|
ImageIndex += 4;
|
|
flash_wr_offset += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fw_img3_addr == flash_wr_offset) {
|
|
if (ImageIndex < frame_size) {
|
|
fw_img3_size = rtk_le32_to_cpu(*((u32*)(ptr+ImageIndex)));
|
|
if (fw_img3_size == 0x1A1A1A1A) {
|
|
// all padding bytes, no image3
|
|
fw_img3_size = 0;
|
|
return rx_len;
|
|
}
|
|
if ((fw_img3_size & 0x03) != 0) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Err#5: fw_img3_addr=0x%x fw_img3_size(%d) isn't 4-bytes aligned\r\n", fw_img3_addr, fw_img3_size);
|
|
fw_img3_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
if (fw_img3_size > (2*1024*1024)) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Image3 to Big: fw_img3_addr=0x%x fw_img2_size(%d) \r\n", fw_img3_addr, fw_img3_size);
|
|
fw_img3_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
// Flash sector erase for image2 writing
|
|
if (flash_erased_addr >= fw_img3_addr) {
|
|
address = flash_erased_addr;
|
|
} else {
|
|
address = fw_img3_addr & (~0xfff); // 4k aligned, 4k is the page size of flash memory
|
|
}
|
|
|
|
while ((address) < (fw_img3_addr+fw_img3_size)) {
|
|
DBG_MISC_INFO("Flash Erase: 0x%x\n", address);
|
|
#if 0
|
|
if ((address & 0xFFFF) == 0) {
|
|
SpicBlockEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x10000; // 1 block = 64k bytes
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SpicSectorEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x1000; // 1 sector = 4k bytes
|
|
}
|
|
}
|
|
flash_erased_addr = address;
|
|
}
|
|
}
|
|
|
|
if (fw_img3_size > 0) {
|
|
// writing image3
|
|
chk_sr = (u32*)((u8*)ptr+ImageIndex);
|
|
chk_dr = (u32*)flash_wr_offset;
|
|
while (ImageIndex < frame_size) {
|
|
FWU_WriteWord(flash_wr_offset, (*((u32*)(ptr+ImageIndex))));
|
|
ImageIndex += 4;
|
|
flash_wr_offset += 4;
|
|
rx_len += 4;
|
|
fw_img3_size -= 4;
|
|
if (fw_img3_size == 0) {
|
|
// Image3 write done,
|
|
break;
|
|
}
|
|
}
|
|
|
|
err_addr = xModem_MemCmp(chk_sr, chk_dr, (flash_wr_offset - (u32)chk_dr));
|
|
if (err_addr) {
|
|
flash_wr_err_cnt++;
|
|
}
|
|
}
|
|
|
|
return rx_len;
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u32 xModem_Frame_ImgAll(char *ptr, unsigned int frame_num, unsigned int frame_size)
|
|
{
|
|
int i;
|
|
u32 address;
|
|
u32 img_size;
|
|
u32 img_addr;
|
|
u32 ImgIdx=0;
|
|
u32 Img1Sign;
|
|
u32 rx_len=0;
|
|
u32 *chk_sr;
|
|
u32 *chk_dr;
|
|
u32 err_addr;
|
|
|
|
if (frame_num == 1) {
|
|
// Check is it the start patten of image1
|
|
start_with_img1 = 1;
|
|
for(i=0; i<4; i++) {
|
|
Img1Sign = rtk_le32_to_cpu(*((u32*)(ptr + i*4)));
|
|
if(Img1Sign != SpicCalibrationPattern[i]) {
|
|
start_with_img1 = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get the image size: the first 4 bytes
|
|
if (start_with_img1) {
|
|
// Image1 + Image2
|
|
// Check the Image1 Signature
|
|
i=0;
|
|
while (ROM_IMG1_VALID_PATTEN[i] != 0xff) {
|
|
if (ptr[i+IMG1_SIGN_OFFSET] != ROM_IMG1_VALID_PATTEN[i]) {
|
|
// image1 validation patten miss match
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Err: Image1 Signature Incorrect\r\n");
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
fw_img2_addr = 0;
|
|
fw_img3_size = 0;
|
|
fw_img3_addr = 0;
|
|
return 0;
|
|
} else {
|
|
// make the signature all 0xff for now, write the signature when image1 download is done
|
|
ptr[i+IMG1_SIGN_OFFSET] = 0xff;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
flash_wr_offset = 0;
|
|
fw_img1_size = rtk_le32_to_cpu(*((u32*)(ptr + 0x10))) + 0x20;
|
|
if ((fw_img1_size & 0x03) != 0) {
|
|
DBG_MISC_WARN("xModem_Frame_ImgAll Err: fw_img1_size(0x%x) isn't 4-bytes aligned\r\n", fw_img1_size);
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
fw_img2_addr = 0;
|
|
fw_img3_size = 0;
|
|
fw_img3_addr = 0;
|
|
return 0;
|
|
}
|
|
address = 0;
|
|
img_size = fw_img1_size;
|
|
img_addr = 0;
|
|
fw_img2_addr = rtk_le16_to_cpu(*((u16*)(ptr + 0x18))) * 1024;
|
|
if (fw_img2_addr == 0) {
|
|
// it's old format: image1 & image2 is cascaded directly
|
|
fw_img2_addr = fw_img1_size;
|
|
}
|
|
fw_img2_size = 0;
|
|
DBG_MISC_INFO("Update Image All: Image1 Size=%d, Image2 Addr=0x%x\r\n", fw_img1_size, fw_img2_addr);
|
|
} else {
|
|
// It's image2(+image3) only
|
|
if (fw_img2_addr == 0) {
|
|
DBG_MISC_WARN("The single-image format in flash now, it cannot just update the image2\r\n");
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
flash_wr_offset = fw_img2_addr;
|
|
fw_img1_size = 0;
|
|
fw_img2_size = rtk_le32_to_cpu(*((u32*)ptr)) + 0x10;
|
|
fw_img3_addr = fw_img2_addr + fw_img2_size;
|
|
if ((fw_img2_size & 0x03) != 0) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Err: fw_img2_size(0x%x) isn't 4-bytes aligned\r\n", fw_img2_size);
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
return rx_len;
|
|
}
|
|
address = fw_img2_addr & (~0xfff); // 4k aligned, 4k is the page size of flash memory
|
|
img_size = fw_img2_size;
|
|
img_addr = fw_img2_addr;
|
|
|
|
DBG_MISC_INFO("Update Image2: Addr=0x%x, Size=%d\r\n", fw_img2_addr, fw_img2_size);
|
|
|
|
}
|
|
|
|
// erase Flash sector first
|
|
while ((address) < (img_addr+img_size)) {
|
|
// DBG_MISC_INFO("Flash Erase: 0x%x\n", address);
|
|
if ((address >= 0x10000 ) && ((address & 0xFFFF) == 0)) {
|
|
SpicBlockEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x10000; // 1 Block = 64k bytes
|
|
}
|
|
else
|
|
{
|
|
SpicSectorEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x1000; // 1 sector = 4k bytes
|
|
}
|
|
}
|
|
flash_erased_addr = address;
|
|
}
|
|
|
|
{
|
|
if (!start_with_img1) {
|
|
if (fw_img2_size > 0) {
|
|
chk_sr = (u32*)((u8*)ptr+ImgIdx);
|
|
chk_dr = (u32*)flash_wr_offset;
|
|
while (ImgIdx < frame_size) {
|
|
FWU_WriteWord(flash_wr_offset, (*((u32*)(ptr+ImgIdx))));
|
|
ImgIdx += 4;
|
|
flash_wr_offset += 4;
|
|
rx_len += 4;
|
|
fw_img2_size -= 4;
|
|
if (fw_img2_size == 0) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
err_addr = xModem_MemCmp(chk_sr, chk_dr, (flash_wr_offset - (u32)chk_dr));
|
|
if (err_addr) {
|
|
flash_wr_err_cnt++;
|
|
}
|
|
}
|
|
} else {
|
|
ImgIdx = 0;
|
|
if (fw_img1_size > 0) {
|
|
// still writing image1
|
|
chk_sr = (u32*)((u8*)ptr+ImgIdx);
|
|
chk_dr = (u32*)flash_wr_offset;
|
|
while (ImgIdx < frame_size) {
|
|
FWU_WriteWord(flash_wr_offset, (*((u32*)(ptr+ImgIdx))));
|
|
ImgIdx += 4;
|
|
flash_wr_offset += 4;
|
|
rx_len += 4;
|
|
fw_img1_size -= 4;
|
|
if (fw_img1_size == 0) {
|
|
// Image1 write done,
|
|
break;
|
|
}
|
|
}
|
|
|
|
err_addr = xModem_MemCmp(chk_sr, chk_dr, (flash_wr_offset - (u32)chk_dr));
|
|
if (err_addr) {
|
|
flash_wr_err_cnt++;
|
|
} else {
|
|
if (fw_img1_size == 0) {
|
|
// Write Image1 signature
|
|
WriteImg1Sign(IMG1_SIGN_OFFSET);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ImgIdx >= frame_size) {
|
|
return rx_len;
|
|
}
|
|
|
|
if (fw_img2_addr == 0) {
|
|
return rx_len;
|
|
}
|
|
|
|
// Skip the section of system data
|
|
if (flash_wr_offset < fw_img2_addr) {
|
|
if ((flash_wr_offset + (frame_size-ImgIdx)) <= fw_img2_addr) {
|
|
flash_wr_offset += (frame_size-ImgIdx);
|
|
return rx_len;
|
|
} else {
|
|
while (ImgIdx < frame_size) {
|
|
if (flash_wr_offset == fw_img2_addr) {
|
|
break;
|
|
}
|
|
ImgIdx += 4;
|
|
flash_wr_offset += 4;
|
|
rx_len += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fw_img2_addr == flash_wr_offset) {
|
|
if (ImgIdx < frame_size) {
|
|
fw_img2_size = rtk_le32_to_cpu(*((u32*)(ptr+ImgIdx))) + 0x10;
|
|
fw_img3_addr = fw_img2_addr + fw_img2_size;
|
|
if ((fw_img2_size & 0x03) != 0) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Err#2: fw_img2_addr=0x%x fw_img2_size(%d) isn't 4-bytes aligned\r\n", fw_img2_addr, fw_img2_size);
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
if (fw_img2_size > (2*1024*1024)) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Image2 to Big: fw_img2_addr=0x%x fw_img2_size(%d) \r\n", fw_img2_addr, fw_img2_size);
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
// Flash sector erase for image2 writing
|
|
if (flash_erased_addr >= fw_img2_addr) {
|
|
address = flash_erased_addr;
|
|
} else {
|
|
address = fw_img2_addr & (~0xfff); // 4k aligned, 4k is the page size of flash memory
|
|
}
|
|
|
|
while ((address) < (fw_img2_addr+fw_img2_size)) {
|
|
DBG_MISC_INFO("Flash Erase: 0x%x\n", address);
|
|
if ((address & 0xFFFF) == 0) {
|
|
SpicBlockEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x10000; // 1 block = 64k bytes
|
|
}
|
|
else
|
|
{
|
|
SpicSectorEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x1000; // 1 sector = 4k bytes
|
|
}
|
|
}
|
|
flash_erased_addr = address;
|
|
}
|
|
}
|
|
|
|
if (fw_img2_size > 0) {
|
|
// writing image2
|
|
chk_sr = (u32*)((u8*)ptr+ImgIdx);
|
|
chk_dr = (u32*)flash_wr_offset;
|
|
while (ImgIdx < frame_size) {
|
|
FWU_WriteWord(flash_wr_offset, (*((u32*)(ptr+ImgIdx))));
|
|
ImgIdx += 4;
|
|
flash_wr_offset += 4;
|
|
rx_len += 4;
|
|
fw_img2_size -= 4;
|
|
if (fw_img2_size == 0) {
|
|
// Image2 write done,
|
|
break;
|
|
}
|
|
}
|
|
|
|
err_addr = xModem_MemCmp(chk_sr, chk_dr, (flash_wr_offset - (u32)chk_dr));
|
|
if (err_addr) {
|
|
flash_wr_err_cnt++;
|
|
}
|
|
}
|
|
|
|
if (ImgIdx >= frame_size) {
|
|
return rx_len;
|
|
}
|
|
|
|
if (fw_img3_addr == flash_wr_offset) {
|
|
if (ImgIdx < frame_size) {
|
|
fw_img3_size = rtk_le32_to_cpu(*((u32*)(ptr+ImgIdx)));
|
|
if (fw_img3_size == 0x1A1A1A1A) {
|
|
// all padding bytes, no image3
|
|
fw_img3_size = 0;
|
|
// DBG_8195A("No Img3\r\n");
|
|
return rx_len;
|
|
}
|
|
if ((fw_img3_size & 0x03) != 0) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Err#5: fw_img3_addr=0x%x fw_img3_size(%d) isn't 4-bytes aligned\r\n", fw_img3_addr, fw_img3_size);
|
|
fw_img3_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
if (fw_img3_size > (2*1024*1024)) {
|
|
DBG_MISC_ERR("xModem_Frame_ImgAll Image3 to Big: fw_img3_addr=0x%x fw_img2_size(%d) \r\n", fw_img3_addr, fw_img3_size);
|
|
fw_img3_size = 0;
|
|
return rx_len;
|
|
}
|
|
|
|
// Flash sector erase for image2 writing
|
|
if (flash_erased_addr >= fw_img3_addr) {
|
|
address = flash_erased_addr;
|
|
} else {
|
|
address = fw_img3_addr & (~0xfff); // 4k aligned, 4k is the page size of flash memory
|
|
}
|
|
|
|
while ((address) < (fw_img3_addr+fw_img3_size)) {
|
|
DBG_MISC_INFO("Flash Erase: 0x%x\n", address);
|
|
if ((address & 0xFFFF) == 0) {
|
|
SpicBlockEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x10000; // 1 block = 64k bytes
|
|
}
|
|
else
|
|
{
|
|
SpicSectorEraseFlashRtl8195A(SPI_FLASH_BASE + address);
|
|
address += 0x1000; // 1 sector = 4k bytes
|
|
}
|
|
}
|
|
flash_erased_addr = address;
|
|
}
|
|
}
|
|
|
|
if (fw_img3_size > 0) {
|
|
// writing image3
|
|
chk_sr = (u32*)((u8*)ptr+ImgIdx);
|
|
chk_dr = (u32*)flash_wr_offset;
|
|
while (ImgIdx < frame_size) {
|
|
FWU_WriteWord(flash_wr_offset, (*((u32*)(ptr+ImgIdx))));
|
|
ImgIdx += 4;
|
|
flash_wr_offset += 4;
|
|
rx_len += 4;
|
|
fw_img3_size -= 4;
|
|
if (fw_img3_size == 0) {
|
|
// Image3 write done,
|
|
break;
|
|
}
|
|
}
|
|
err_addr = xModem_MemCmp(chk_sr, chk_dr, (flash_wr_offset - (u32)chk_dr));
|
|
if (err_addr) {
|
|
flash_wr_err_cnt++;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return rx_len;
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
s32
|
|
xModem_Init_UART_Port(u8 uart_idx, u8 pin_mux, u32 baud_rate)
|
|
{
|
|
if (uart_idx <= XMODEM_UART_2) {
|
|
// update firmware via generic UART
|
|
pxmodem_uart_adp = &xmodem_uart_adp; // we can use dynamic allocate to save memory
|
|
// pxmodem_uart_adp = RtlZmalloc(sizeof(HAL_RUART_ADAPTER));
|
|
xmodem_uart_init(uart_idx, pin_mux, baud_rate);
|
|
xmodem_uart_func_hook(&(xMCtrl.ComPort));
|
|
} else if(uart_idx == XMODEM_LOG_UART) {
|
|
// update firmware via Log UART
|
|
// DiagPrintf("Open xModem Transfer on Log UART...\r\n");
|
|
// xmodem_loguart_init();
|
|
xmodem_loguart_init(baud_rate);
|
|
xmodem_loguart_func_hook(&(xMCtrl.ComPort));
|
|
// DiagPrintf("Please Start the xModem Sender...\r\n");
|
|
} else {
|
|
// invalid UART port
|
|
DBG_MISC_ERR("xModem_Init_UART_Port: Invaild UART port(%d)\n", uart_idx);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
VOID
|
|
xModem_DeInit_UART_Port(u8 uart_idx)
|
|
{
|
|
if (uart_idx <= XMODEM_UART_2) {
|
|
xmodem_uart_deinit();
|
|
} else if (uart_idx == XMODEM_LOG_UART) {
|
|
xmodem_loguart_deinit();
|
|
}
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
__weak s32
|
|
UpdatedImg2AddrValidate(
|
|
u32 Image2Addr,
|
|
u32 DefImage2Addr,
|
|
u32 DefImage2Size
|
|
)
|
|
{
|
|
if (Image2Addr == 0xffffffff) {
|
|
// Upgraded Image2 isn't exist
|
|
return 0; // invalid address
|
|
}
|
|
|
|
if ((Image2Addr & 0xfff) != 0) {
|
|
// Not 4K aligned
|
|
return 0; // invalid address
|
|
}
|
|
|
|
if (Image2Addr <= DefImage2Addr) {
|
|
// Updated image2 address must bigger than the addrss of default image2
|
|
return 0; // invalid address
|
|
}
|
|
|
|
if (Image2Addr < (DefImage2Addr+DefImage2Size)) {
|
|
// Updated image2 overlap with the default image2
|
|
return 0; // invalid address
|
|
}
|
|
|
|
return 1; // this address is valid
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
__weak s32
|
|
Img2SignValidate(
|
|
u32 Image2Addr
|
|
)
|
|
{
|
|
u32 img2_sig[3];
|
|
s32 sign_valid=0;
|
|
|
|
// Image2 header: Size(4B) + Addr(4B) + Signature(8B)
|
|
img2_sig[0] = HAL_READ32(SPI_FLASH_BASE, Image2Addr + 8);
|
|
img2_sig[1] = HAL_READ32(SPI_FLASH_BASE, Image2Addr + 12);
|
|
img2_sig[2] = 0; // end of string
|
|
|
|
if (_memcmp((void*)img2_sig, (void*)Img2Signature, 8)) {
|
|
DBG_MISC_INFO("Invalid Image2 Signature:%s\n", img2_sig);
|
|
} else {
|
|
sign_valid = 1;
|
|
}
|
|
|
|
return sign_valid;
|
|
|
|
}
|
|
|
|
|
|
FWU_TEXT_SECTION
|
|
VOID
|
|
MarkImg2SignOld(
|
|
u32 Image2Addr
|
|
)
|
|
{
|
|
u32 img2_sig;
|
|
|
|
_memcpy((void*)&img2_sig, (void*)Img2Signature, 4);
|
|
*((char*)(&img2_sig)) = '0'; // '8' -> the latest image; '0' -> the older image
|
|
FWU_WriteWord((Image2Addr + 8), img2_sig);
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
VOID
|
|
WriteImg1Sign(
|
|
u32 Image2Addr
|
|
)
|
|
{
|
|
u32 img1_sig;
|
|
|
|
_memcpy((void*)&img1_sig, (void*)ROM_IMG1_VALID_PATTEN, 4);
|
|
FWU_WriteWord(IMG1_SIGN_OFFSET, img1_sig);
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
VOID
|
|
WriteImg2Sign(
|
|
u32 Image2Addr
|
|
)
|
|
{
|
|
u32 img2_sig[2];
|
|
|
|
_memcpy((void*)img2_sig, (void*)Img2Signature, 8);
|
|
FWU_WriteWord((Image2Addr + 8), img2_sig[0]);
|
|
FWU_WriteWord((Image2Addr + 12), img2_sig[1]);
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u32
|
|
SelectImg2ToUpdate(
|
|
u32 *OldImg2Addr
|
|
)
|
|
{
|
|
u32 DefImage2Addr=0xFFFFFFFF; // the default Image2 addr.
|
|
u32 SecImage2Addr=0xFFFFFFFF; // the 2nd image2 addr.
|
|
u32 ATSCAddr=0xFFFFFFFF;
|
|
u32 UpdImage2Addr; // the addr of the image2 to be updated
|
|
u32 DefImage2Len;
|
|
#ifdef CONFIG_UPDATE_TOGGLE_IMG2
|
|
u32 SigImage0,SigImage1;
|
|
#endif
|
|
|
|
*OldImg2Addr = 0;
|
|
DefImage2Addr = (HAL_READ32(SPI_FLASH_BASE, 0x18)&0xFFFF) * 1024;
|
|
if ((DefImage2Addr != 0) && ((DefImage2Addr < (16*1024*1024)))) {
|
|
// Valid Default Image2 Addr: != 0 & located in 16M
|
|
DefImage2Len = HAL_READ32(SPI_FLASH_BASE, DefImage2Addr);
|
|
|
|
// Get the pointer of the upgraded Image2
|
|
SecImage2Addr = HAL_READ32(SPI_FLASH_BASE, FLASH_SYSTEM_DATA_ADDR);
|
|
|
|
if (UpdatedImg2AddrValidate(SecImage2Addr, DefImage2Addr, DefImage2Len)) {
|
|
UpdImage2Addr = SecImage2Addr; // Update the 2nd image2
|
|
#ifdef CONFIG_UPDATE_TOGGLE_IMG2
|
|
// read Part1/Part2 signature
|
|
SigImage0 = HAL_READ32(SPI_FLASH_BASE, DefImage2Addr + 8);
|
|
SigImage1 = HAL_READ32(SPI_FLASH_BASE, DefImage2Addr + 12);
|
|
|
|
DBG_8195A("\n\rPart1 Sig %x", SigImage0);
|
|
if(SigImage0==0x30303030 && SigImage1==0x30303030)
|
|
ATSCAddr = DefImage2Addr; // ATSC signature
|
|
else if(SigImage0==0x35393138 && SigImage1==0x31313738)
|
|
*OldImg2Addr = DefImage2Addr; // newer version, change to older version
|
|
else
|
|
UpdImage2Addr = DefImage2Addr; // update to older version
|
|
|
|
SigImage0 = HAL_READ32(SPI_FLASH_BASE, SecImage2Addr + 8);
|
|
SigImage1 = HAL_READ32(SPI_FLASH_BASE, SecImage2Addr + 12);
|
|
DBG_8195A("\n\rPart2 Sig %x\n\r", SigImage0);
|
|
if(SigImage0==0x30303030 && SigImage1==0x30303030)
|
|
ATSCAddr = SecImage2Addr; // ATSC signature
|
|
else if(SigImage0==0x35393138 && SigImage1==0x31313738)
|
|
*OldImg2Addr = SecImage2Addr;
|
|
else
|
|
UpdImage2Addr = SecImage2Addr;
|
|
|
|
// update ATSC clear partitin first
|
|
if(ATSCAddr != ~0x0){
|
|
*OldImg2Addr = UpdImage2Addr;
|
|
UpdImage2Addr = ATSCAddr;
|
|
}
|
|
#endif // end of SWAP_UPDATE, wf, 1006
|
|
} else {
|
|
// The upgraded image2 isn't exist or invalid so we can just update the default image2
|
|
UpdImage2Addr = DefImage2Addr; // Update the default image2
|
|
#ifdef CONFIG_UPDATE_TOGGLE_IMG2
|
|
*OldImg2Addr = DefImage2Addr;
|
|
#endif
|
|
}
|
|
} else {
|
|
UpdImage2Addr = 0;
|
|
}
|
|
|
|
return UpdImage2Addr;
|
|
}
|
|
|
|
|
|
FWU_TEXT_SECTION
|
|
void OTU_FW_Update(u8 uart_idx, u8 pin_mux, u32 baud_rate)
|
|
{
|
|
u32 wr_len;
|
|
u32 OldImage2Addr=0; // the addr of the image2 will become old one
|
|
SPIC_INIT_PARA SpicInitPara;
|
|
|
|
fw_img1_size = 0;
|
|
fw_img2_size = 0;
|
|
fw_img2_addr = 0;
|
|
fw_img3_size = 0;
|
|
fw_img3_addr = 0;
|
|
flash_wr_offset = 0;
|
|
flash_erased_addr = 0;
|
|
start_with_img1 = 0;;
|
|
flash_wr_err_cnt = 0;
|
|
|
|
// Get the address of the image2 to be updated
|
|
SPI_FLASH_PIN_FCTRL(ON);
|
|
if (!SpicFlashInitRtl8195A(SpicOneBitMode)){
|
|
SPI_FLASH_PIN_FCTRL(OFF);
|
|
DBG_MISC_ERR("OTU_FW_Update: SPI Init Fail!!!!!!\n");
|
|
return;
|
|
}
|
|
SpicWaitWipDoneRefinedRtl8195A(SpicInitPara);
|
|
|
|
printf("FW Update Over UART%d, PinMux=%d, Baud=%d\r\n", uart_idx, pin_mux, baud_rate);
|
|
fw_img2_addr = SelectImg2ToUpdate(&OldImage2Addr);
|
|
|
|
// Start to update the Image2 through xModem on peripheral device
|
|
printf("FW Update Image2 @ 0x%x\r\n", fw_img2_addr);
|
|
// We update the image via xModem on UART now, if we want to uase other peripheral device
|
|
// to update the image then we need to redefine the API
|
|
if (xModem_Init_UART_Port(uart_idx, pin_mux, baud_rate) < 0) {
|
|
return;
|
|
}
|
|
|
|
// xModemStart(&xMCtrl, xMFrameBuf, xModem_Frame_ImgAll); // Support Image format: Image1+Image2 or Image2 only
|
|
xModemStart(&xMCtrl, xMFrameBuf, xModem_Frame_Img2); // Support Image format: Image2 only
|
|
// xModemStart(&xMCtrl, xMFrameBuf, xModem_Frame_Dump); // for debugging
|
|
wr_len = xModemRxBuffer(&xMCtrl, (2*1024*1024));
|
|
xModemEnd(&xMCtrl);
|
|
|
|
xModem_DeInit_UART_Port(uart_idx);
|
|
|
|
if ((wr_len > 0) && (flash_wr_err_cnt == 0)) {
|
|
// Firmware update OK, now write the signature to active this image
|
|
WriteImg2Sign(fw_img2_addr);
|
|
#ifdef CONFIG_UPDATE_TOGGLE_IMG2
|
|
// Mark the other image2 as old one by modify its signature
|
|
if (OldImage2Addr != 0) {
|
|
printf("Mark Image2 @ 0x%x as Old\r\n", OldImage2Addr);
|
|
MarkImg2SignOld(OldImage2Addr);
|
|
}
|
|
#endif
|
|
}
|
|
printf("OTU_FW_Update Done, Write Len=%d\n", wr_len);
|
|
SPI_FLASH_PIN_FCTRL(OFF);
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u8 OTU_check_gpio(void)
|
|
{
|
|
#ifdef CONFIG_GPIO_EN
|
|
HAL_GPIO_PIN GPIO_Pin;
|
|
u8 enter_update;
|
|
|
|
GPIO_Pin.pin_name = HAL_GPIO_GetIPPinName_8195a(0x21);; //pin PC_1
|
|
GPIO_Pin.pin_mode = DIN_PULL_HIGH;
|
|
|
|
_pHAL_Gpio_Adapter = &gBoot_Gpio_Adapter;
|
|
|
|
HAL_GPIO_Init_8195a(&GPIO_Pin);
|
|
if (HAL_GPIO_ReadPin_8195a(&GPIO_Pin) == GPIO_PIN_LOW) {
|
|
enter_update = 1;
|
|
}
|
|
else {
|
|
enter_update = 0;
|
|
}
|
|
HAL_GPIO_DeInit_8195a(&GPIO_Pin);
|
|
|
|
_pHAL_Gpio_Adapter = NULL;
|
|
return enter_update;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
u8 OTU_check_uart(u32 UpdateImgCfg){
|
|
|
|
if(((UpdateImgCfg>>4)&0x03) == 2){
|
|
ACTCK_SDIOD_CCTRL(OFF);
|
|
|
|
/* SDIO Function Disable */
|
|
SDIOD_ON_FCTRL(OFF);
|
|
SDIOD_OFF_FCTRL(OFF);
|
|
|
|
// SDIO Pin Mux off
|
|
SDIOD_PIN_FCTRL(OFF);
|
|
}
|
|
|
|
if (xModem_Init_UART_Port(((UpdateImgCfg>>4)&0x03), (UpdateImgCfg&0x03), 115200) < 0) {
|
|
return 0;
|
|
}
|
|
|
|
|
|
char ch;
|
|
u8 x_count = 0;
|
|
int timeout1 = 500;
|
|
while (timeout1 != 0) {
|
|
if (xMCtrl.ComPort.poll()) {
|
|
ch = xMCtrl.ComPort.get();
|
|
if(ch != 0x78){
|
|
xModem_DeInit_UART_Port(((UpdateImgCfg>>4)&0x03));
|
|
return 0;
|
|
}
|
|
x_count ++;
|
|
if(x_count == 5){
|
|
xModem_DeInit_UART_Port(((UpdateImgCfg>>4)&0x03));
|
|
return 1;
|
|
}
|
|
}
|
|
HalDelayUs(200);
|
|
timeout1--;
|
|
}
|
|
|
|
if(!x_count){
|
|
xModem_DeInit_UART_Port(((UpdateImgCfg>>4)&0x03));
|
|
return 0;
|
|
}
|
|
|
|
int timeout2 = 4500;
|
|
while (timeout2 != 0) {
|
|
if (xMCtrl.ComPort.poll()) {
|
|
ch = xMCtrl.ComPort.get();
|
|
if(ch != 0x78){
|
|
xModem_DeInit_UART_Port(((UpdateImgCfg>>4)&0x03));
|
|
return 0;
|
|
}
|
|
x_count ++;
|
|
if(x_count == 5)
|
|
{
|
|
xModem_DeInit_UART_Port(((UpdateImgCfg>>4)&0x03));
|
|
return 1;
|
|
}
|
|
}
|
|
HalDelayUs(200);
|
|
timeout2--;
|
|
}
|
|
|
|
xModem_DeInit_UART_Port(((UpdateImgCfg>>4)&0x03));
|
|
return 0;
|
|
}
|
|
|
|
FWU_TEXT_SECTION
|
|
void OTU_Img_Download(u8 uart_idx, u8 pin_mux, u32 baud_rate,
|
|
u32 start_offset, u32 start_addr, u32 max_size)
|
|
{
|
|
SPIC_INIT_PARA SpicInitPara;
|
|
u32 wr_len;
|
|
u8 is_flash=0;
|
|
|
|
if (xModem_Init_UART_Port(uart_idx, pin_mux, baud_rate) < 0) {
|
|
return;
|
|
}
|
|
|
|
DBG_MISC_INFO("Image Download: StartOffset=%d StartAddr=0x%x MaxSize=%d\r\n", start_offset, start_addr, max_size);
|
|
|
|
fw_img2_addr = start_addr;
|
|
flash_wr_offset = start_offset;
|
|
fw_img2_size = max_size;
|
|
|
|
if ((start_addr & 0xFF000000) == SPI_FLASH_BASE) {
|
|
// it's going to write the Flash memory
|
|
if (((start_addr & 0x03) != 0) || ((start_offset&0x03) != 0)) {
|
|
DiagPrintf("StartAddr(0x%x), StartOffset(0x%x) Must 4-bytes Aligned\r\n", start_addr, start_offset);
|
|
return;
|
|
}
|
|
SPI_FLASH_PIN_FCTRL(ON);
|
|
if (!SpicFlashInitRtl8195A(SpicOneBitMode)){
|
|
DBG_MISC_ERR("OTU_FW_Update: SPI Init Fail!!!!!!\n");
|
|
SPI_FLASH_PIN_FCTRL(OFF);
|
|
return;
|
|
}
|
|
is_flash = 1;
|
|
SpicWaitWipDoneRefinedRtl8195A(SpicInitPara);
|
|
fw_img2_addr = start_addr & 0x00FFFFFF;
|
|
xModemStart(&xMCtrl, xMFrameBuf, xModem_Frame_FlashWrite);
|
|
} else {
|
|
xModemStart(&xMCtrl, xMFrameBuf, xModem_Frame_MemWrite);
|
|
}
|
|
wr_len = xModemRxBuffer(&xMCtrl, ((((max_size+flash_wr_offset-1)>>7)+1) << 7));
|
|
xModemEnd(&xMCtrl);
|
|
|
|
xModem_DeInit_UART_Port(uart_idx);
|
|
|
|
DBG_MISC_INFO("OTU_Img_Download Done, Write Len=%d\n", wr_len);
|
|
|
|
if (is_flash) {
|
|
SPI_FLASH_PIN_FCTRL(OFF);
|
|
}
|
|
}
|
|
|
|
#endif //#if CONFIG_PERI_UPDATE_IMG
|
|
|
|
#endif // #if CONFIG_UART_SOCKET
|