新闻中心

EEPW首页 > PCI设备初始化编程的过程

PCI设备初始化编程的过程

——
作者:时间:2007-03-06来源:电子产品世界收藏

这里以PowerPC上的为例,介绍编程的过程,其他CPU的PCI基本框架是类似的.

PowerPC的PCI控制器符合PCI2.2标准,以下使我解读的VxWorks系统程序代码分析

主要执行过程

1.PCI设备

文件romInit.s中romInit()=======>文件bootInit.c中romStart()========>文件usrConfig.c中的usrInit()======>文件syslib.c中的sysHwInit()

syslib.c

void sysHwInit (void){ //用于系统硬件

......................

//和网络,PCI有关的函数

#ifdef INCLUDE_EMAC_NETWORK


//初始化媒介访问层MAL(控制EMAC和Buffer Descriptor BD间的数据传输)

malReset();

#endif

#ifdef INCLUDE_PCI
pciInit(); //初始化PCI
#endif

#ifdef INCLUDE_NETWORK
sysNetHwInit(); //初始化网络接口
#endif

}

 

//该函数初始化PCI

PCI的地址转换形式:

CPU->PCI (master/initiator) 地址转换
PCI->CPU (slave/target) 地址转换

void pciInit()
{

unsigned short temp_short;

/*
* 初始化并 disable 所有的 PCI Master regions(三个),
little endian,直接对寄存器R操作 */

sysPciOutLong(PMM0MA, PMM_UNUSED);
sysPciOutLong(PMM1MA, PMM_UNUSED);
sysPciOutLong(PMM2MA, PMM_UNUSED);

/*

解释:

PMM0MA:地址 0xEE400004, 控制PLB上的映射到PCI存储空间的range0大小和属性.

在PLB上共有三个ranges, PMM1MA代表区域rang1, PMM2MA代表区域range2.

PMM_UNUSED: 0x00000000

range的定义

range: map PLB======>PCI 64-bit

PPBridge(PLB和PCI设备间的接口)responds as a target on the PLB bus in servral address ranges.

These ranges allow a PLB Master to configure the PPBridge and to cause PPBridge to generate Mem I/O,interrupt acknowledge and special cycles to PCI bus.

*/


/*
* Initially disable PCI Target region 2 to start. Region 1 is hardwired
* to always be active.
*/

//PTM2MS:地址 0xEF400038,定义PCI存储空间的大小.这里清零

sysPciOutLong(PTM2MS, PTM_UNUSED);

/*
* Drive PCI 重新置位. 用于热启动,置位必须保持1ms
*/


//解释: 从PCI寄存器PCI_CFG_BRIDGE_OPT2中读出数值

其中 PPC405GP_PCI_BUSDEVFUNC = 0x00000000;

PCI_CFG_BRIDGE_OPT2 = 0x60

temp_short = pciConfigIn(PPC405GP_PCI_BUSDEVFUNC, PCI_CFG_BRIDGE_OPT2, 2);

//把读出的值放回寄存器PCI_CFG_BRIDGE_OPT2中

pciConfigOut(PPC405GP_PCI_BUSDEVFUNC, PCI_CFG_BRIDGE_OPT2,
temp_short | 0x1000, 2);

sysLocalDelay(1); /* 延时1ms kernel may not be up yet!! */

//再放一次

pciConfigOut(PPC405GP_PCI_BUSDEVFUNC, PCI_CFG_BRIDGE_OPT2, temp_short, 2);


/*
* 设置PCI Master(PMM),这是从本地存储地址Local memory address到PCI空间的映射 * CPU->PCI,参见文件 See config.h中的定义,参考 BSP配置文件
*/{{分页}}


//说明sysPciOutLong(A,B);放B到A

//PMM region 0

//PMM0LA 0xEF400000, PMM0_LOCAL_ADRS 0x80000000

sysPciOutLong(PMM0LA, PMM0_LOCAL_ADRS);

//PMM0PCILA 0xEF400008, PMM0_PCI_LOW_ADRS 0x80000000

sysPciOutLong(PMM0PCILA, PMM0_PCI_LOW_ADRS);

//PMM0PCIHA 0xEF40000C, PMM0_PCI_HIGH_ADRS 0x00000000

sysPciOutLong(PMM0PCIHA, PMM0_PCI_HIGH_ADRS);

//PMM0MA 0xEF400004,

//PMM0_PCI_MASK_ATTRIB PMM_MASK_512MB|PMM_ENABLE

sysPciOutLong(PMM0MA, PMM0_PCI_MASK_ATTRIB);


sysPciOutLong(PMM1LA, PMM1_LOCAL_ADRS); /* PMM region 1 */
sysPciOutLong(PMM1PCILA, PMM1_PCI_LOW_ADRS);
sysPciOutLong(PMM1PCIHA, PMM1_PCI_HIGH_ADRS);
sysPciOutLong(PMM1MA, PMM1_PCI_MASK_ATTRIB);

sysPciOutLong(PMM2LA, PMM2_LOCAL_ADRS); /* PMM region 2 */
sysPciOutLong(PMM2PCILA, PMM2_PCI_LOW_ADRS);
sysPciOutLong(PMM2PCIHA, PMM2_PCI_HIGH_ADRS);
sysPciOutLong(PMM2MA, PMM2_PCI_MASK_ATTRIB);


/*
* 设置 PCI Target (PTM). 这是从PCI的地址到本地地址的映射
* 参见文件 config.h中的定义
* 如果不是用 region 2, 必须保证它的使能位关闭,region 1硬件连接使能.
*/


//PTM1LA 0xEF400034, PTM1_LOCAL_ADRS 0x00000000

sysPciOutLong(PTM1LA, PTM1_LOCAL_ADRS);

//PTM1MS 0xEF400030,

sysPciOutLong(PTM1MS, PTM1_SIZE_ATTRIB);

...................

/*
* 写 405GP PCI 设置寄存器.
* 使能 405GP,使其成为PCI总线的一个master (PMM).
* 使能 405GP 作为一个PCI memory target (PTM).
*/

temp_short = pciConfigIn(PPC405GP_PCI_BUSDEVFUNC, PCI_CFG_COMMAND, 2);
temp_short = temp_short | PCI_CMD_MASTER_ENABLE | PCI_CMD_MEM_ENABLE;
pciConfigOut(PPC405GP_PCI_BUSDEVFUNC, PCI_CFG_COMMAND, temp_short, 2);

..........................


/*
* 作为PCI的主机host扫描PCI总线,查找PCI设备. 并为每个设备分配唯一的PCI或I/O地址* 空间
*/

pciScan(0);
return;
}

 

//pciScan() - 扫描PCI总线,根据PCI ID和 vender ID查找设置现有的PCI设备

void pciScan
(
int busNum
)
{
int Device;
int BusDevFunc;


/*
* 从device 1开始,查找每个Slot,如有PCI设备,分配内存和I/O, 405GP是 device 0.
*/


for (Device = 1; Device <= WALNUT_NUM_PCI_SLOTS; Device++)
{
BusDevFunc = (busNum << 16) | (Device << 11);

//如果Device存在
if (pciConfigIn(BusDevFunc, PCI_CFG_VENDOR_ID,2) != 0xFFFF)
{
#ifdef PCIDEBUG
printf("* * * * * * * * * * * * * * * * *n");
printf("Device %d is presentn",Device);
#endif
switch( pciConfigIn(BusDevFunc, PCI_CFG_SUBCLASS, 2) )
{

case 0x0604: /* PCI-PCI Bridge */
break;

default:
pciDumpDevice(BusDevFunc); //打印 Vendor ID, Device ID

//调用pciConfigDevice(BusDevFunc, 6)对PCI设备进行设置


pciConfigDevice(BusDevFunc, 6);
}
}
else
{
#ifdef PCIDEBUG
printf("Device %d not presentn",Device);
#endif
}
}
}

//pciConfigDevice - 为PCI设备配置内存和I/O方式

void pciConfigDevice(int BusDevFunc,int NumBaseAddr)
{
int AddrSlot;
int i;
unsigned long AddrDesc;
unsigned long AddrProg;
unsigned long Min_Gnt_Val;


// NumBaseAddr = 6
for (AddrSlot = 0; AddrSlot < NumBaseAddr; AddrSlot++)
{

// PCI_CFG_BASE_ADDRESS_0 = 0x10 将0xFFFFFFFF写入 PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot)
pciConfigOut(BusDevFunc, PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
0xFFFFFFFF, 4);

// 从PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot)读出数据
AddrDesc = pciConfigIn(BusDevFunc,
PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
4);


// 如果数据是0,没有PCI设备,继续循环搜索
if (AddrDesc == 0) /* unimplemented, stop looking */
continue;

#ifdef PCIDEBUG
printf("Read Base Addr Reg %d = 0x%08xn",AddrSlot,AddrDesc);
#endif
/* 如果AddrDesc的位0是0,表示是Mem方式 */
if ((AddrDesc & 1) == 0)
{
AddrDesc &= 0xFFFFFFF0;

for (i = 0; (AddrDesc & 1) != 1; i++) //查询Mem的大小
AddrDesc = AddrDesc >> 1;

AddrDesc = 1 << i;

if ((unsigned long)AddrDesc < 4096)
AddrDesc = 4096;
#ifdef PCIDEBUG
printf(" PCI Memory space = 0x%x bytes n",AddrDesc);
#endif
for (AddrProg = PCI_MEMORY_START;
AddrProg < LowestMemAddr;
AddrProg += AddrDesc);{{分页}}

pciConfigOut(BusDevFunc, PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
AddrProg, 4);
LowestMemAddr = AddrProg + AddrDesc;
}
else /* I/O 方式*/
{
AddrDesc &= 0xFFFFFFFC;

for (i = 0; (AddrDesc & 1) != 1; i++) //查询I/O空间的大小
AddrDesc = AddrDesc >> 1;

AddrDesc = 1 << i;
#ifdef PCIDEBUG
printf(" PCI I/O space = 0x%x bytes n",AddrDesc);
#endif
for (AddrProg = PCI_IO_REGION_1_START;
AddrProg < LowestIOAddr;
AddrProg += AddrDesc);

pciConfigOut(BusDevFunc, PCI_CFG_BASE_ADDRESS_0 + (4*AddrSlot),
AddrProg, 4);
LowestIOAddr = AddrProg + AddrDesc;
}

}


/*
* Read Min_Gnt(0x3eh) register value and write it to
* the Latency Timer(0xdh) register
*/
Min_Gnt_Val = 0x80;
pciConfigOut(BusDevFunc, PCI_CFG_LATENCY_TIMER, Min_Gnt_Val, 1);

}

2.PCI网卡驱动调用过程

sysNet.c用于初始化系统的网络

文件sysNet.c中void sysNetHwInit(void)定义了,这里以AMD PCI网卡97x为例:

#ifdef INCLUDE_PCI_NETWORK
unsigned char sysEnetAddr [6]; /* 网卡的 MAC 地址 */
char sys97xLoadString[100]; /* 需要为初始化加载的字符串 */
#endif

#ifdef INCLUDE_PCI_NETWORK
STATUS sysIn97xEndBldLoadStr() // 生成网络初始化的字符串


函数sysIn97xEndBldLoadStr()调用pciFindDevice()

STATUS pciFindDevice //用Vendor ID和Device ID查找PCI网卡
(
int VendorID,
int DeviceID,
int index,
int *busDevFunc
)
{
int Device;
int tmpBusDevFunc;
int tmpIndex;
int busNum;

tmpIndex = 0;
for (busNum = MaxBusNum; busNum >= 0; busNum--) //查讯Slot
{
for (Device = 0; Device <= WALNUT_NUM_PCI_SLOTS; Device++)
{
tmpBusDevFunc = (busNum << 16) | (Device << 11);

// 如果指定寄存器中Vendor ID和Device ID正确,赋值给*busDevFunc,返回

if (pciConfigIn(tmpBusDevFunc, PCI_CFG_VENDOR_ID, 2) == VendorID
&& pciConfigIn(tmpBusDevFunc, PCI_CFG_DEVICE_ID, 2) == DeviceID)
{
if (tmpIndex == index)
{
*busDevFunc = tmpBusDevFunc;
return (OK);
}
else /* have a match, but not correct index */
{
tmpIndex++;
}
} } }
return (ERROR); }


sysln97xEndBldLoadStr(..)查找PCI以太网卡,得到PCI网卡的基地址,根据PCI插槽Slot选择正确的中断向量,读网卡的MAC地址,然后调用ln97xEndLoad

STATUS sysln97xEndBldLoadStr ( void )
{
int i;
int intvec;
int intlvl;
ULONG pciMemAddr;
unsigned int busDevFunc;


/*
* 在PCI总线上查找设置第一个AMD设备.
* 如果发现存在,函数pciFindDevice 为busDevFunc赋值
*/

if (pciFindDevice(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_79C97X, 0, &busDevFunc))
return(ERROR);

/*
* 97x的基地址 1 包含PCI存储空间的基地址.
*/
pciMemAddr = pciConfigIn(busDevFunc, PCI_CFG_BASE_ADDRESS_1, 4);
pciMemAddr &= 0xFFFFFFF0;

/*
* 设置PCI定时器反应时间为50 Set latency timer to 50.
*/
pciConfigOut(busDevFunc, PCI_LATENCY_TIMER, 50, 1);

/*
* 每个PCI插槽连接到中断控制器的管脚不一样,中断取决于网卡插在哪一个PCI插槽上.
*/
switch ((busDevFunc & 0x0000F800) >> 11) /* Strip off just the device */
{
case 1 : intvec = INT_VEC_PCI_SLOT3; /* 插槽 3 */
intlvl = INT_LVL_PCI_SLOT3;
break;
case 2 : intvec = INT_VEC_PCI_SLOT2; /* 插槽 2 */
intlvl = INT_LVL_PCI_SLOT2;
break;
case 3 : intvec = INT_VEC_PCI_SLOT1; /* 插槽 1 */
intlvl = INT_LVL_PCI_SLOT1;
break;
case 4 : intvec = INT_VEC_PCI_SLOT0; /* 插槽 0 */
intlvl = INT_LVL_PCI_SLOT0;
break;
default : return(ERROR); /* Not possible, error */

}

/*
* 使能PCI Mem cycles 和 总线 Master 操作
*/
pciConfigOut(busDevFunc, PCI_CFG_COMMAND,
PCI_CMD_MEM_ENABLE | PCI_CMD_MASTER_ENABLE, 2);

/*
* 得到网卡的MAC地址
*/
for (i=0; i<6; i++)
sysEnetAddr = sysInByte(pciMemAddr+APROM01+i);

/*
* 生成初始化字符串,如一下形式:
*
* <devMemAddr>:<devIoAddr>:<pciMemBase>:<vecNum>:<intLvl>:<memAdrs>:
* <memSize>:<memWidth>:<csr3b>:<offset>:<flags>
*
* 这个字符串将在函数muxEndLoad()中加载
*/

sprintf(sys97xLoadString, "0x%x:0x%x:0:%d:%d:0x%x:0:0x%x:0:0:0",
(unsigned int)pciMemAddr, NONE, intvec, intlvl,
NONE,NONE);

return(OK);

}



关键词: PCI设备 初始化

评论


相关推荐

技术专区

关闭