某展锐平台有这样一个PCIe的客户需求:
通过执行 AT+QCFG=“pcie/mode” 查询模块当前模式,0 表示PCIe EP 模式;1 表示PCIe RC 模式。
若模块处于RC 模式,执行AT+QCFG=“pcie/mode”,0 并重启模块将PCIe 模式切换至EP 模式;同样,也可以通过命令由EP模式切换到RC模式。
通过验证,设计方案如下实现:
1.在atrouter中写标志到miscdata分区
2.uboot中读取miscdata中的标志,并添加cmdline,传递到内核
3.在设备树中添加RC和EP节点,在对应的驱动中解析cmdline,来区分是否加载对应的驱动
详细步骤如下:
1.在atrouter中写miscdata分区
本来是计划写item项的,但是后面读取时发现在uboot中并没有对应的接口,太麻烦。考虑到uboot中有对应的miscdata读取接口,于是用写miscdata分区方式实现。
数据写到768 * 1024+1024的位置,如果是EP模式,写入EP_mode,如果是RC模式,写入RC_mode。
代码片段如下:
//#include "ATSWDUMPHandler.h"
#include <stdbool.h>
#include <errno.h>
#include <time.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef __packed
#define __packed __attribute__((packed))
#endif
#define CONFIG_NAND
#ifdef CONFIG_NAND
#include <ubi-user.h>
#include <sys/ioctl.h>
#endif
#ifdef CONFIG_NAND
#define PATH_MISCDATA_PREFIX "/dev/ubi0_"
#else
#define PATH_MISCDATA_PREFIX "/dev/disk/by-partlabel/"
#define PATH_PROC_PREFIX "/etc/productinfo"
#endif
///source/unisoc/engpc/modules/libmiscdata/miscdata.c
#define NAND_MISCDATA_FILESIZE (1024 * 1024)
#define NAND_MISCDATA_CUSTOMER_START (768 * 1024)
#define NAND_MISCDATA_CUSTOMER_CAN_USR ((NAND_MISCDATA_FILESIZE) - (NAND_MISCDATA_CUSTOMER_START))
//下面这个宏定义为你要放的数据存在客户可用的区域的起始地址(至少为0),如果有多个量要存储,记住不要重叠,会覆盖
#define Nand_MISCDATA_INFO_OFFSET 1024
#define NAND_MISCDATA_SWDUMP ((NAND_MISCDATA_CUSTOMER_START)+(Nand_MISCDATA_INFO_OFFSET))
static int swdumplength = 7;
unsigned long get_file_size(const char *path) {
/*
unsigned long filesize = -1;
struct stat statbuff;
if(stat(path, &statbuff) < 0){
return filesize;
}else{
filesize = statbuff.st_size;
}
return filesize;
*/
return NAND_MISCDATA_FILESIZE;
}
int read_miscdata(char *databuf, int data_len) {
AT_LOGD("enter the function read_miscdata");
AT_LOGD("data_len is %d",data_len);
int ret = 0;
int len;
char prop[128] = { 0 };
char miscdata_path[128] = { 0 };
int fd = -1;
sprintf(prop, "%s", PATH_MISCDATA_PREFIX);
sprintf(miscdata_path, "%smiscdata", prop);
fd = open(miscdata_path, O_RDONLY);
if (fd >= 0) {
AT_LOGD("%s open Ok miscdata_path = %s ", __FUNCTION__, miscdata_path);
len = read(fd, databuf, data_len);
AT_LOGD("databuf is %s",databuf);
if (len <= 0) {
ret = -2;
AT_LOGD("%s read fail miscdata_path = %s ", __FUNCTION__, miscdata_path);
}
close(fd);
} else {
AT_LOGD("%s open fail miscdata_path = %s ", __FUNCTION__, miscdata_path);
ret = -3;
}
return ret;
}
int write_miscdata(char *databuf, int data_len) {
AT_LOGD("write_miscdata");
int ret = 0;
int len;
int fd = -1;
char prop[128] = { 0 };
char miscdata_path[128] = { 0 };
sprintf(prop, "%s", PATH_MISCDATA_PREFIX);
sprintf(miscdata_path, "%smiscdata", prop);
fd = open(miscdata_path, O_WRONLY);
if (fd >= 0) {
AT_LOGD("%s open Ok miscdata_path = %s ", __FUNCTION__, miscdata_path);
#ifdef CONFIG_NAND
__s64 up_sz = data_len;
ioctl(fd, UBI_IOCVOLUP, &up_sz);
#endif
len = write(fd, databuf, data_len);
if (len <= 0) {
ret = -2;
AT_LOGD("%s read fail miscdata_path = %s ", __FUNCTION__, miscdata_path);
}
fsync(fd);
close(fd);
} else {
AT_LOGD("%s open fail miscdata_path = %s ", __FUNCTION__, miscdata_path);
ret = -3;
}
return ret;
}
int read_miscdata_with_offset(int offset, char *databuf, int data_len) {
int ret = 0;
int len;
int fd = -1;
char prop[128] = { 0 };
char miscdata_path[128] = { 0 };
int szFile = 0;
char* buff = NULL;
sprintf(prop, "%s", PATH_MISCDATA_PREFIX);
sprintf(miscdata_path, "%smiscdata", prop);
#ifdef CONFIG_NAND
szFile = get_file_size(miscdata_path);
AT_LOGD("%s: szFile = %d", __FUNCTION__, szFile);
if(szFile <= 0) {
return -2;
}
buff = (char*)malloc(szFile+1);
if (buff == NULL) {
AT_LOGD("%s: malloc fail\n", __FUNCTION__);
return -3;
}
memset(buff, 0, szFile+1);
ret = read_miscdata(buff, szFile);
if (0 == ret) {
memcpy(databuf, buff+offset, data_len);
}
free(buff);
buff = NULL;
return ret;
#else
fd = open(miscdata_path, O_RDONLY);
if (fd >= 0) {
AT_LOGD("%s open Ok miscdata_path = %s ", __FUNCTION__,
miscdata_path);
if (lseek(fd, offset, SEEK_SET) == -1) {
AT_LOGD("%s offset:%d seek failed! = %s ", __FUNCTION__, offset,
miscdata_path);
ret = -4;
close(fd);
return ret;
}
len = read(fd, databuf, data_len);
if (len <= 0) {
ret = -2;
AT_LOGD("%s read fail miscdata_path = %s ", __FUNCTION__,
miscdata_path);
}
close(fd);
} else {
AT_LOGD("%s open fail miscdata_path = %s ", __FUNCTION__,
miscdata_path);
ret = -3;
}
#endif
return ret;
}
int write_miscdata_with_offset(int offset, char *databuf, int data_len) {
int ret = 0;
int len;
int fd = -1;
char prop[128] = { 0 };
char miscdata_path[128] = { 0 };
int szFile = 0;
char* buff = NULL;
AT_LOGE("write_miscdata_with_offset");
sprintf(prop, "%s", PATH_MISCDATA_PREFIX);
//sprintf(prop, "%s", PATH_PROC_PREFIX);
sprintf(miscdata_path, "%smiscdata", prop);
//sprintf(miscdata_path, "%s", prop);
#ifdef CONFIG_NAND
AT_LOGD("define CONFIG_NAND,write_miscdata_with_offset");
szFile = get_file_size(miscdata_path);
AT_LOGE("%s: szFile = %d", __FUNCTION__, szFile);
if(szFile <= 0) {
return -2;
}
buff =(char*)malloc(szFile+1);
if (buff == NULL) {
AT_LOGE("%s: malloc fail\n", __FUNCTION__);
return -3;
}
memset(buff, 0, szFile+1);
ret = read_miscdata(buff, szFile);
if(0 == ret) {
memcpy(buff+offset, databuf, data_len);
ret = write_miscdata(buff, szFile);
} else {
ret = -4;
}
free(buff);
buff = NULL;
return ret;
#else
fd = open(miscdata_path, O_RDWR); //O_WRONLY
if (fd >= 0) {
AT_LOGE("%s open Ok miscdata_path = %s ", __FUNCTION__,miscdata_path);
if (lseek(fd, offset, SEEK_SET) == -1) {
AT_LOGE("%s offset:%d seek failed! = %s ", __FUNCTION__, offset,miscdata_path);
ret = -4;
close(fd);
return ret;
}
len = write(fd, databuf, data_len);
if (len <= 0) {
ret = -2;
AT_LOGE("%s write fail miscdata_path = %s return = %d ", __FUNCTION__,miscdata_path, len);
}
fsync(fd);
close(fd);
}
else {
AT_LOGE("%s open fail miscdata_path = %s ", __FUNCTION__,miscdata_path);
ret = -3;
}
#endif
return ret;
}
……
设置模式代码如下:
string str_ep("EP_mode");
string str_rc("RC_mode");
char* charswdump = const_cast<char*>(str_rc.c_str());
swdumplength = str_rc.size();
AT_LOGE(" %s:str.size = %d \n", __FUNCTION__, swdumplength);
if (swdumplength > NAND_MISCDATA_CUSTOMER_CAN_USR){
atresponse->AppendContentResponse("\n+the parameter is too long");
atresponse->BuildCommonResponse(ATC_RSP_RET_CME_ERROR, AT_ERROR_4);
return -1;
}
if(0 == write_miscdata_with_offset(NAND_MISCDATA_SWDUMP, charswdump, swdumplength)){
atresponse->AppendContentResponse("\n+write miscdata success");
atresponse->BuildCommonResponse(ATC_RSP_RET_OK, 0);
}else{
atresponse->AppendContentResponse("\nwrite miscdata fail!");
atresponse->BuildCommonResponse(ATC_RSP_RET_CME_ERROR, AT_ERROR_4);
}
读取使用如下方式:
char* pswdump = new char[swdumplength+1];
if((swdumplength==0) || 0!= read_miscdata_with_offset(NAND_MISCDATA_SWDUMP, pswdump, swdumplength)){
atresponse->AppendContentResponse("\nread miscdata fail!");
}
else
{
string strpar_read="READ:";
AT_LOGE("In buildTestResponse,pswdump is %s",pswdump);
//0 ~ (swdumplength-1) is read data,add \0
pswdump[swdumplength]='\0';
strpar_read += pswdump;
atresponse->AppendContentResponse(strpar_read.c_str());
}
验证结果:
[2022-06-22_14:28:08:986]AT+QCFG="pcie/mode",0
[2022-06-22_14:28:09:286]+write miscdata success
[2022-06-22_14:28:09:286]OK
[2022-06-22_14:28:15:686]AT+QCFG="pcie/mode"
[2022-06-22_14:28:15:786]READ:EP_mode
[2022-06-22_14:28:15:786]OK
[2022-06-22_14:28:19:886]AT+QCFG="pcie/mode",1
[2022-06-22_14:28:20:086]+write miscdata success
[2022-06-22_14:28:20:086]OK
[2022-06-22_14:28:24:386]AT+QCFG="pcie/mode"
[2022-06-22_14:28:24:486]READ:RC_mode
[2022-06-22_14:28:24:486]OK
2.在uboot中读取miscdata分区数据。
这里使用已有的接口即可,但要注意读取的起始地址要一致。
bool pcie_mode_ep_flag = false;
#define SET_PCIE_MODE_LEN 0x4
#define SET_PCIE_MODE_OFFSET (768 * 1024 + 1024)
static int get_miscdata_pcie_flag(char *out)
{
if (0 != common_raw_read("miscdata", SET_PCIE_MODE_LEN,
(uint64_t)(SET_PCIE_MODE_OFFSET), out)) {
errorf("partition <miscdata> read error\n");
return -1;
}
return 0;
}
调用:
u32 get_pcie_mode = 0;
bool pcie_mode_ep_flag = false;
err = get_miscdata_pcie_flag((char *)(&get_pcie_mode));
if (err < 0) {
debugf("Reading pcie mode flag from miscdata partition failed!\n");
}
else
{
debugf("Reading pcie mode: 0x%x\n", get_pcie_mode); //EP_mode-->m_PE
if((get_pcie_mode & 0xFFFF) == 0x5045)
pcie_mode_ep_flag = true;
}
这里只读4个字节,如果写入的是EP_mode,由于小端模式,这里读到的out是0x6d5f5045,换成ASCII码就是m_PE。
可以根据这个设置传递cmdline到内核。
int fdt_fixup_pcie_ep_mode(void *fdt)
{
char buf[256];
int str_len;
int ret;
memset(buf, 0, 20);
sprintf(buf, "pcie_mode=EP");
str_len = strlen(buf);
buf[str_len] = '\0';
ret = fdt_chosen_bootargs_append(fdt, buf, 1);
return ret;
}
……
extern bool pcie_mode_ep_flag;
void fdt_fixup_all(u8 *fdt_blob)
{
……
if(pcie_mode_ep_flag)
{
debugf("sy:set pcie_mode_ep_flag \n");
fdt_fixup_pcie_ep_mode(fdt_blob);
}
else
{
debugf("sy:set pcie_mode_rc_flag \n");
fdt_fixup_pcie_rc_mode(fdt_blob);
}
验证EP模式时,传递cmdlime为pcie_mode=EP,RC模式时为pcie_mode=RC。
3.解析cmdline,加载驱动。
在设备树中需要把RC和EC的节点都加上,即pcie0和pcie1都设为okay。在驱动中去匹配是否加载:
unsigned int pcie_ep_mode = 0;
//解析cmdline
static __init int get_pcie_ep_mode(char *str)
{
//if str1=str2,return 0; str1<str2 return -; str1>str2 return +
if (str != NULL && !strncmp(str, "EP", strlen("EP")))
pcie_ep_mode = 1;
else
pcie_ep_mode = 0;
printk(KERN_ERR "get_pcie_ep_mode=%d\n", pcie_ep_mode);
return 0;
}
__setup("pcie_mode=", get_pcie_ep_mode);
static int sprd_pcie_probe(struct platform_device *pdev)
{
……
if(1 == pcie_ep_mode)
{
printk(KERN_ERR "sprd_pcie_probe: pcie RC mode skip\n");
return 0;
}
……
}
EP驱动的加载:
extern unsigned int pcie_ep_mode;
static int sprd_pcie_ep_probe(struct platform_device *pdev)
{
……
//begin:added for pcie mode
if(0 == pcie_ep_mode)
{
printk(KERN_ERR "sprd_pcie_ep_probe: pcie EP mode skip\n");
return 0;
}
//end:added for pcie mode
至此驱动加载完成。