基于 NXP i.MX8QM SAF775C Firmware Upgrade

关键字 :i.MX8QMSAF775C
一.  前言

SAF775C 是恩智浦半导体 (NXP) 将汽车收音机和音响系统完全集成在单个芯片上的 IC ,是恩智浦广受欢迎的汽车收音机和音频 DSP 产品系列的第三代。SAF775C 的固件升级是通过上位机工具进行升级 ,常用到的升级是对音频 DSP 部分进行升级 ,在机器做成成品后通过上位机升级会非常麻烦 , 我们接下来要分享得部分是如何在 iMX8系统中对 SAF775C 的固件部分进行升级 ,以下内容部分由 Pual 和 Watson 共同完成 ,Watson 进行整理 ,调试部分由 Baker 和 Watson 现场调试 ,调试结果得到客户确认 !


二.  环境搭建

2.1 Flash 的烧录流程


                                                    图1. FLASH 烧录流程

2.2 确认 i.MX8 和 SAF775C 之间的通信已经打通

SAF775C 支持 I2C 通信和 SPI 通信 ,我们目前使用的是 SPI 接口和SAF775C 进行通信和数据传输

2.3  D3DL.cpp 文件 :

D3DL.cpp 这个文件是运行在上位机升级工具的源文件 ,请找原厂或是供应商提供 ,接下来我们会把下载驱动部分移植到 Android 平台


三. SAF775C升级固件程序移植

3.1 加载升级固件 

原厂提供的代码是在 Windows 上执行的文件打开读写操作,需要将对应的操作函数修改为可在 Android 平台上执行的函数,否则编译会提示找不到对应函数导致编译失败

对以下内容修进行修改:

static void D3DL_LoadImage(unsigned char nUsbUnitId)

{

    CFile   spi_binaryfile;

    uint8_t  i;

……………………………

if (spi_binaryfile.Open(pImageName[i], CFile::modeRead))  //打开文件操作

{

        filesize = (unsigned long)spi_binaryfile.GetLength(); //获取文件大小

        cnt = spi_binaryfile.Read(image, 4); // 读取文件前四个字节内容

        ……………………………………………………

        cnt = fread(&image[4], 1, filesize-4, fp);  //  读取文件剩余内容

        ……………………………………………………

        spi_binaryfile.Close();  // Close file


对以上的内容修改为:

static void D3DL_LoadImage(unsigned char nUsbUnitId) {

    uint8_t  i;

………………………………………

fp = fopen(pImageName[i], "r");  // 打开文件操作

//  获取文件大小

fseek(fp, 0, SEEK_END);  

filesize = ftell(fp);    

// 读取文件前四个字节内容

fseek(fp, 0, SEEK_SET);       

cnt = fread(image, 1, 4, fp);

………………………………………

cnt = fread(&image[4], 1, filesize-4, fp); //读取文件剩余内容

fclose(fp);  // Close file

D3DL_LoadFlashImage()函数关于文件加载部分的修改 ,可以参考上面内容修改


3.2 升级固件传输 :

打开的 Binary 文件需要通过 IOCTL 的方式传递给驱动层 ,驱动层接收数据后通过 SPI 总线把数据写入到 SAF775C Flash 中

3.2.1 应用层需要对驱动层进行数据传输 ,状态读取和 Reset 操作 , 下面是对 IOCTL 功能进行定

struct saf_data {

unsigned int len;

unsigned char data[32];

void *buf;

};

#define    SAF775D_IOCTL_MAGIC         'D'

#define IOCTL_D3DL_I2CSPI_WRITE_DATA       _IOW(SAF775D_IOCTL_MAGIC, 0xA0, struct saf_data)

#define IOCTL_D3DL_I2CSPI_READ_DATA        _IOW(SAF775D_IOCTL_MAGIC, 0xA1, struct saf_data)

#define IOCTL_D3DL_I2CSPI_RESET              _IOW(SAF775D_IOCTL_MAGIC, 0xA2, int)

#define IOCTL_D3DL_I2CSPI_READ_INT               _IOW(SAF775D_IOCTL_MAGIC, 0xA3, int)


3.2.2  对 I2CSPI_Read 和 I2CSPI_Write 函数进行封装

m_pPort->I2CSPI_ReadINT(nUsbUnitId) ,m_pPort->I2CSPI_Write(nUsbUnitId, msg, Cnt),

m_pPort->I2CSPI_Read(nUsbUnitId,msg, Cnt) ,函数进行封装 ,使它们具备原有函数读取状态 ,传递参数的功能

nUsbUnitId 这个变量是 PC 端 USB 转串口时用到的 ID ,在 Android 平台不会使用到 ,在封装函数的时候把这个选项去掉了

①  m_pPort->I2CSPI_ReadINT(nUsbUnitId) 函数的封装

unsigned char Wpi_I2CSPI_ReadINT(void){

      unsigned char status = 0;

      ioctl(fd, IOCTL_D3DL_I2CSPI_READ_INT,&status);

      return status;

}


② m_pPort->I2CSPI_Write(nUsbUnitId, msg, Cnt) 函数的封装

unsigned char Wpi_I2CSPI_Write_Data(unsigned char *Msg, unsigned int Lens){

      struct saf_data ImageData;

      ImageData.len = Lens;

      ImageData.buf = Msg;

      ioctl(fd, IOCTL_D3DL_I2CSPI_WRITE_DATA, &ImageData);

      return 0;

}

③ m_pPort->I2CSPI_Read(nUsbUnitId,msg, Cnt) 函数的封装

unsigned char Wpi_I2CSPI_Read_Data(unsigned char *Msg, unsigned int Lens)

{

      unsigned char  i;

      struct saf_data sMsg;

       sMsg.len = Lens;

 

      ioctl(fd, IOCTL_D3DL_I2CSPI_READ_DATA, &sMsg);

      for (i = 0 ;i < sMsg.len; i++)

      {

           Msg[i]=sMsg.data[i];

       }

        

       return 0;

}

④ Reset()函数

static void D3DL_Reset(void)

{

  ioctl(fd, IOCTL_D3DL_I2CSPI_RESET, 0);

}


3.2.3 驱动部分 IOCTL 的操作

① Wpi_I2CSPI_ReadINT()对应驱动的IOCTL_D3DL_I2CSPI_READ_INT 操作

case IOCTL_D3DL_I2CSPI_READ_INT:                                            

     int gpio4_status;                                              

     if (copy_from_user(&gpio4_status, args, sizeof(gpio4_status))) 

return ERROR;                                              

     gpio4_status = gpio_get_value(saf775d_GPIO4);                  

     copy_to_user((void *)args, &gpio4_status, sizeof(gpio4_status));

break;


② Wpi_I2CSPI_Write_Data()对应驱动的IOCTL_D3DL_I2CSPI_WRITE_DATA 操作

case IOCTL_D3DL_I2CSPI_WRITE_DATA:                                                

     safdbgprt(KERN_INFO"saf775d %s cmd:SAF775D_SPI_WRITE \n",__func__);

     struct saf_data write_data;                                    

u8 *temp_flash;                                                

int i = 0;                                                     

if (copy_from_user(&write_data, (void *)args, sizeof(write_data)))

return ERROR;                                                              

temp_flash = (u8 *)kmalloc(write_data.len * sizeof(u8),GFP_KERNEL);

if (copy_from_user((u8 *)temp_flash, (u8 *)write_data.buf, write_data.len))

return ERROR;                                                                                                                          

saf775drv_write(temp_flash, write_data.len);  // 把数据写到 SAF775C                                                                                                

kfree(temp_flash);                                                                                                                             

break; 

③ Wpi_I2CSPI_Read_Data()对应驱动的IOCTL_D3DL_I2CSPI_READ_DATA 操作

case IOCTL_D3DL_I2CSPI_READ_DATA:                                                  

safdbgprt(KERN_INFO"saf775d %s cmd:SAF775D_SPI_READ \n",__func__);

struct saf_data read_data;                                     

if (copy_from_user(&read_data, (void *)args, sizeof(read_data)))

 return ERROR;                                                                                                                            

saf775drv_read(read_data.data, read_data.len);    // 从 SAF775C 读取状态             

printk("[baker_k]spi_read_finished \n");                       

copy_to_user((void *)args, &read_data, sizeof(read_data));                                                                                    

break; 

 

3.3  Firmware App 编译

2.3.1完成了固件代码的移植后,接下来就是对代码进行编译,生成 Android 可执行文件。



需要创建 Android.mk 文件,代码如下 :

LOCAL_PATH := $(call my-dir)

 

include $(CLEAR_VARS)

LOCAL_SRC_FILES := imx8_775C_flash.c

#LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LIBDRM_IMX)/libdrm-imx/ $(LIBDRM_IMX)/libdrm-imx/include/drm/

LOCAL_MULTILIB := both

LOCAL_CFLAGS += -DBUILD_FOR_ANDROID

LOCAL_SHARED_LIBRARIES := libutils libc

LOCAL_STATIC_LIBRARIES := libdrm

LOCAL_MODULE := imx8_775C_flash

LOCAL_MODULE_STEM_32 := imx8_775C_flash_32

LOCAL_MODULE_STEM_64 := imx8_775C_flash_64

LOCAL_MODULE_TAGS := tests

LOCAL_VENDOR_MODULE := true

include $(BUILD_EXECUTABLE)

3.3.2 将 imx8_775C_flash.c 和 Android.mk 文件放到Android SDK 新建目录下:

android_build/external/775C ,进入 android_build 目录下执行如下命令开始编译:

source build/envsetup.sh

mmm external/775C

3.3.2 编译完成后会生成对应的可执行文件:out/target/product/mek_8q/vendor/bin/imx8_775C_flash_64


                                                                          图2.  imx8_775C_flash_64 编译结果

四. 固件烧录调试

4.1 固件烧录参数

4.1.1 以 load_app_binary_image 的命令为例进行解释

$ imx8_775C_flash_64 -L 6 SAF775C_ABB_E7A0_nxp_pro_encrypted.bin 0

imx8_775C_flash_64 调用上面生成的 SAF775C Firmware Upgrade 应用程序

-L 表示 : firmware image loading

6  表示 : 6-> ICC ARM LOWER image

0  表示 : address offset是0

-R 表示 : boot the target

4.1.2 通过应用程序执行imx8_775C_flash_64 -L 6 SAF775C_ABB_E7A0_nxp_pro_encrypted.bin 0 命令后可以看到 file size = 37212

                                          
                                                                图3. 获取 file size

4.1.3 在驱动层IOCTL_D3DL_I2CSPI_WRITE_DATA 插入 log 信息 ,可以看到传输的数据长度为 0 – 37211

U8  *ImageData = temp_flash;

for (i =0; i < write_data.len ; i++ )

printk("ImageData[%d] = 0x%02X", i,ImageData[i]);


                    图4.  ImageData 长度

通过以上信息可以确认升级文件已经成功导入到驱动层


4.2 固件烧录程序调试

4.2.1 每次对 SAF775C 进行读写操作后都要读取 GPIO4 的状态 ,确认为高后再进行下一步操作

bool  Gpio4Status = Wpi_I2CSPI_ReadINT();

if (Gpio4Status == false)

{

  // SAF775C Busy

}

4.2.2 下面是SAF775C 驱动部分烧录 Flash 的 Source Code 

Load SAF775C_Arm_Ffs_nxp_pro_encrypted.bin , Boot 成功后SAF775C 才能正常运行并接收 Erase ,Flash 指令

int download_Flash_Image_all(void)

{

    int ret;

   // reset saf775d

    reset_saf775d_gpio_long();

    // host boot, load APP

    ret = load_app_binary_image("Dione_Arm_Ffs_nxp_pro_encrypted.bin");

    if (ret != 0)  return ret;

    msleep(10);

    //boot from host

    ret = boot_target();

    if(ret != 0) return ret;

    msleep(30);

    ups.percent = 5;//finish 5%

    note_percent();

    //erase Flash of SAF775X

    ret = erase_flash();

    if(ret != 0)  return ret;

    ups.percent = 10;//finish 10%

    note_percent();


4.3 使用脚本升级

为了方便升级 ,可以参考下面的脚本方式进行升级

#!/bin/sh

imx8_775C_flash_64 -v

imx8_775C_flash_64 -L 6 /data/arm/SAF775C_Arm_Ffs_nxp_pro_encrypted.bin 0

imx8_775C_flash_64 -R

imx8_775C_flash_64 -E

imx8_775C_flash_64 -F 0 /data/radio/dsp0/SAF775C_BBP_E7A0_nxp_pro_encrypted.bin  0

imx8_775C_flash_64 -F 1 /data/radio/dsp1/SAF775C_BBP_E7A1_nxp_pro_encrypted.bin  0

imx8_775C_flash_64 -F 2 /data/radio/dsp2/SAF775C_BBP_E7A2_nxp_pro_encrypted.bin  0

imx8_775C_flash_64 -F 3 /data/audio/dsp0/SAF775C_ABB_E7A0_nxp_pro_encrypted.bin  0

imx8_775C_flash_64 -F 4 /data/audio/dsp1/SAF775C_ABB_E7A1_nxp_pro_encrypted.bin  0

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram0_offset0_nxp_pro_encrypted.bin  0 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram0_offset15_nxp_pro_encrypted.bin 1 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram1_offset16_nxp_pro_encrypted.bin 2 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_dram1_offset31_nxp_pro_encrypted.bin 3 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset80_nxp_pro_encrypted.bin  4 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset95_nxp_pro_encrypted.bin  5 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset110_nxp_pro_encrypted.bin 6 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset125_nxp_pro_encrypted.bin 7 

imx8_775C_flash_64 -F 5 /data/ecnr_ss/ECNR_SS_iram_offset140_nxp_pro_encrypted.bin 8 

imx8_775C_flash_64 -F 6 /data/arm/SAF775C_ICC_ARM0_upper_nxp_pro_encrypted.bin 0

imx8_775C_flash_64 -F 6 /data/arm/SAF775C_ICC_ARM0_lower_nxp_pro_encrypted.bin 1


i.MX8QM SAF775C 固件升级 ,首先 确认  i.MX8 和 SAF775C 之间 I2C 或是 SPI 通信正常 ,接着对 D3DL.cpp 内容进行移植 ,然后在 Android 端编写应用程序 ,并打通 APP 和 Kernel 间的通信 ,以上完成后开始烧录固件 ,通过以上内容相信大家已经了解怎么通过 Android 端编写应用程序来对 SAF775C 固件升级


五. 参考文档

【1】NXP UM SAF775X Hifi Software User Manual.pdf   Rev. 1.0 — 05 June 2017

【2】NXP SAF775X_UM_DIONEDL  Dione Download Tool  Rev. <0.1> — 14 June 2017


★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论