logo资料库

PCI设备初始化.pdf

第1页 / 共31页
第2页 / 共31页
第3页 / 共31页
第4页 / 共31页
第5页 / 共31页
第6页 / 共31页
第7页 / 共31页
第8页 / 共31页
资料共31页,剩余部分请下载后查看
SBD_DISPLAY("DONE",0);
SBD_DISPLAY("BEV1",0);
CPU_FlushCache();
SBD_DISPLAY("BEV0",0);
BONITO_PCIMAP_PCIMAP_2;
BONITO_PCIBASE0 = PCI_LOCAL_MEM_PCI_BASE;
BONITO_PCIBASE1 = PCI_LOCAL_MEM_ISA_BASE;
BONITO_PCIBASE2 = PCI_LOCAL_MEM_PCI_BASE + 0x10000000;
PCIMAP寄存器
_pci_scan_dev(pb, i, 8, init);//from 8+11, hu mingchang
PCI设备配置头的前16个字节
Pcimap_cfg的格式
/* * Set up memory address decoders to map entire memory. * But first move away bootrom map to high memory. */ 下面我们跟踪 initmips 的执行来观察系统初始化的过程。 void initmips(unsigned int memsz) { memorysize=(memsz&0x0000ffff) << 20;//recover to original size:256M memorysize_high=((memsz&0xffff0000)>>16) << 20;//0 /* * Probe clock frequencys so delays will work properly. */ tgt_cpufreq(); SBD_DISPLAY("DONE",0); /* * Init PMON and debug */ cpuinfotab[0] = &DBGREG; dbginit(NULL); /* * Set up exception vectors. */ SBD_DISPLAY("BEV1",0); bcopy(MipsException, (char *)TLB_MISS_EXC_VEC, MipsExceptionEnd - MipsException); bcopy(MipsException, (char *)GEN_EXC_VEC, MipsExceptionEnd - 第二章 PCI 设备初始化 系统刚上电时,CPU 从 0xbfc0.0000 开始执行。这个地址在 Rom 空间中, 在完成 TLB,Cache,UART 等初始化后,CPU 就将代码拷到 0x8010.0000 开始 的 RAM 空间(这个地址是编译 Pmon 时分配符号_start 的),然后跳转到 initmips(), 开始在内存空间的执行。 执行 initmips 之前,CPU 做的初始化只是初步的,其作用只是为 CPU 在内 存中运行做一些必要的准备。主要的初始化工作:PCI 设备的扫描、空间映射、 资源分配都是 initmips()函数所完成的。 MipsException); CPU_FlushCache(); CPU_SetSR(0, SR_BOOT_EXC_VEC); SBD_DISPLAY("BEV0",0); /* Launch! */ 1
main(); } 首 先 是 获 取 CPU 的 时 钟 频 率 , 这 是 tgt_cpufreq() 完 成 的 。 它 定 义 在 tgt_machdep.c 中。主要的方法就是先读取 COP0 中的 count 寄存器,然后延时一 段时间,再读取 count 寄存器。两次的差值乘以 2 就是这段时间内 cpu 的时钟周 期数。另外,CMOS 中有个实时钟,在延时前后读取当前时间该相减,就可以知 道延时的准确时间。从而计算出 cpu 的时钟频率。全局变量 md_cpufreq 记录了 cpu 频率值,md_pipefreq 是流水线的频率,它们的值分别是 500MHZ 和 1000MHZ; 接着调用的是 dbginit(),这是最要的一个函数,几乎所有的初始化代码都 由他直接或间接调用。  构造函数(constructor)的执行。 Dbginit()调用的第一个函数是__init()。这个函数的过程很简单,它就是将所 有的 constructor 的函数执行一遍,建立一些基本的数据结构。在 pmon 中有三类 constructor 函数,它们都是静态函数。 1) 命令处理初始化函数,位于 pmon/cmds 目录下,其名称都叫 init_cmd()。 2) 文 件 系 统 初 始 化 函 数 。pmon/fs 目 录 下 。 函 数 名 称 叫 init_fs() 或 者 init_xxxfs()。 const char *name; //命令的名称 const char *opts; //参数 const Optdesc *optdesc; //命令参数的 option const char *desc; //命令描述 int (*func) __P((int, char *[])); //处理函数 int minac; //最小参数个数 int maxac; //最大参数个数 int flag; 3) 可执行文件类型初始化。在 pmon/loader 目录下。函数名称叫 init_exec() Pmon 中定义了大量的命令,每个命令都对应一个 Cmd 类型的结构。该结构 的含义如下。 typedef struct Cmd { #define CMD_REPEAT 1 /* Command is repeatable */ #define CMD_HIDE 2 #define CMD_ALIAS } Cmd; CmdTable 是一个指针数组。对每一个命令,CmdTable 中都有一个指针指向 它对应的 Cmd 结构。Init_cmd()所作的就是将各个 Cmd 结构的地址填入 CmdTable 中。 /* Command is hidden */ 4 /* Alias for another command name */ Pmon 中所支持的每一个文件系统都有一个相应的数据结构来表示。对于磁 盘文件系统,这个结构是 DiskFileSystem;对于其他文件系统,这个结构叫做 FileSystem。这两个结构的成员主要是一些函数指针,分别指向文件系统的 open, read,write,ioctl,lseek 及 close 函数。 2
文件系统初始化就是代表各个文件系统的数据结构插入到相应链表。对于磁 盘文件系统,链表的头指针式 DiskFileSystems。对于其他的文件系统,头指针是 FileSystems。这样当需要对某个文件操作(包括虚拟文件,如与用户交互的终端 termio)。通过这两个链表可以找到相应的结构,在通过里面的函数指针就可以对 文件进行具体操作了。 char *execname; /*文件类型,如 bin ,elf 等*/ long (*loader) __P((int , char *, int *, int ));/*载入文件类型,并设置执行条 Pmon 可以载入执行几种格式的文件。其中包括 elf 可执行文件,二进制文 件(称为 Raw binary file)。对每一个支持的文件类型,有一个 ExecType 类型的的 结构。这个结构定义如下: typedef struct ExecType { 件*/ #define EXECFLAGS_NONE 0x0000 #define EXECFLAGS_NOAUTO 0x0001 } ExecType; 由于每种执行文件的格式不一样,因此对它们的载入执行也不一样,函数指 针 loader 就是文件的转载寒暑。对于二进制文件(bin),这个函数是 load_bin。二进 制文件是最简单的,load_bin 所作的只不过就是将执行文件读入到指定地址。Elf 文件比较复杂,它的载入函数是 load_elf()。这个函数比较复杂,但过程还是很 简单的,就是依据 elf 文件头的内容载入各个程序段的内容,并返回可执行文件 开始执行的地址。 int flags; SLIST_ENTRY(ExecType) /* Don't auto load */ i_next;  环境初始化 envint()设置所有的环境变量。最多可以设置 64 个环境变量。每个环境变 量对应一个 envpair 结构。Envvar 是一个 envpair 类型的数组。envinit()就是根据 标准环境变量的值(数组 stdenvtab)来初始化 envvar 数组。更改环境变量的值可以 改变 pmon 中命令的行为以及一些系统参数。  设备初始化 设备初始化主要就是 PCI 设备的初始化。这是由 tgt_devinit()完成的。 tgt_devinit 又调用了_pci_businit()。 PCI 设备初始化分为两步,第一步是北桥初始化。第二步是设备初始化。在 Pmon 中,每个 PCI 设备都对应一个 pci_device 结构(包括 pci-pci 桥);每个 pci 总线都对应一个 pci_bus 结构; pa; //设备的一些信息,如 id,class,中断线 struct pci_attach_args struct pci_device { // min_gnt; max_lat; int_line; stat; unsigned char unsigned char unsigned char pcireg_t 3
u_int8_t intr_routing[4]; struct pci_bridge bridge; struct pci_bus *pcibus; struct pci_device *next; struct pci_device *parent; }; Min_gnt,Max_lat 是 PCI 设备和时间相关的参数。Min_gnt 说明在 33Mhz 时 钟频率下,一个 burst period 所要的时间。而 Max_lat 说明了设备访问 PCI 总线 的频率。从它们的定义可以看出这两个参数和设备的带宽密切相关。 Stat 则说明了设备所处的状态(是否可用,是否可以作为主设备等)。 由于所有的设备都连接在总线上,而总线又总是通过 pci 桥联在系统中,因 此结构中还有一个 pci_bridge 结构 bridge。如果该设备本身是一个 pci 桥,则 bridge 代表了它自身。 Pcibus 指向设备所在的总线。所有的 pci 设备通过 next 指针形成一个链表。 最后,parent 指向该设备的父设备。 /* next bus pointer */ struct pci_bus { }; 可以看到 pci_bus 中很多成员和 pci_device 相同,这是因为 pci_bus 结构的信 struct pci_bus *next; u_int8_t min_gnt; /* largest min grant */ u_int8_t max_lat; /* smallest max latency */ u_int8_t devsel; /* slowest devsel */ u_int8_t fast_b2b; /* support fast b2b */ u_int8_t prefetch; /* support prefetch */ u_int8_t freq66; /* support 66MHz */ u_int8_t width64; /* 64 bit bus */ u_int8_t bus; u_int8_t ndev; /* # devices on bus */ u_int8_t def_ltim; /* default ltim counter */ u_int8_t max_ltim; /* maximum ltim counter */ int32_t bandwidth; /* # of .25us ticks/sec @ 33MHz */ paddr_t minpcimemaddr; /* PCI allocation min mem for bus */ paddr_t nextpcimemaddr;/* PCI allocation max mem for bus */ paddr_t minpciioaddr; nextpciioaddr; paddr_t pci_mem_base; paddr_t paddr_t pci_io_base; /* PCI allocation min i/o for bus */ /* PCI allocation max i/o for bus */ 息是和它所连接的设备相关的。 对 pci_bus 结 构 要 说 明 的 地 方 是 minpcimemadd , nextpcimemaddr , minpciioadd,nextpciioaddr。Pci 设备所要的 memory 空间和 IO 空间都是有限的。 Minpcimemaddr 是 PCI 总线所能分配的最小 memory 地址;minpciioaddr 是能分 配的最小 IO 地址。Pmon 才用的空间分配方法是从高地址到低地址。每次为设 备分配 mem 空间都是从 nextpcimemaddr 开始往下分配,分配 io 空间同样是从 4
nextpciioaddr 开始往下分配。分配的时候要注意不能超过最低可用的地址。 1. 北桥初始化 北桥提供了 CPU 和 PCI 设备相互访问的通道,实现了 CPU 空间和 PCI 空间 的映射。_pci_hwinit() 具体地完成了这个工作。 所有的 pci 设备通过总线和 pci 桥联成一个树状结构。北桥 Bonito 是这棵树 的根,与北桥直接相连的总线就是 pcibus0。_pci_hwinit()的工作就是建立北 桥和 pcibus0 的数据结构,同时进行 CPU 和 PCI 的地址映射。 if (!initialise) { return(0); } /*pcireg_t stat;*/ struct pci_device *pd; struct pci_bus *pb; int initialise; bus_space_tag_t iot; bus_space_tag_t memt; int _pci_hwinit(initialise, iot, memt) { /* * PCI Bus 0 */ pd = pmalloc(sizeof(struct pci_device)); pb = pmalloc(sizeof(struct pci_bus)); if(pd == NULL || pb == NULL) { } >bus_base); 5 pci_local_mem_pci_base = PCI_LOCAL_MEM_PCI_BASE; /* * Allocate and initialize PCI bus heads. */ printf("pci: can't alloc memory. pci not initialized\n"); return(-1); pd->pa.pa_flags = PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED; pd->pa.pa_iot = pmalloc(sizeof(bus_space_tag_t)); pd->pa.pa_iot->bus_reverse = 1; pd->pa.pa_iot->bus_base = BONITO_PCIIO_BASE_VA; //printf("pd->pa.pa_iot=%p,bus_base=0x%x\n",pd->pa.pa_iot,pd->pa.pa_iot-
pd->pa.pa_memt = pmalloc(sizeof(bus_space_tag_t)); pd->pa.pa_memt->bus_reverse = 1; pd->pa.pa_memt->bus_base = PCI_LOCAL_MEM_PCI_BASE; pd->pa.pa_dmat = &bus_dmamap_tag; pd->bridge.secbus = pb; _pci_head = pd; 从 上 面 可 以 看 到 , 北 桥 的 pa.pa_flags 被 设 置 成 IO_ENABLED 和 MEM_ENABLED,这就表示可以进行 IO 和内存访问。 Pa.pa_iot->bus_base 被设置成 BONITO_PCI_IO_BASE_VA,这个值被定义为 0xbfd0.0000,它在 CPU 的 Kseg1 区间,因此是不经过 TLB 转换的。它的物理地 址是 0x1fd0.0000。Bontio 规范中规定这个地址开始的1M 空间是 PCI 的 IO 空间。 一个 pci 桥的配置体内有三个域和总线相关。分别是上游总线(Primary bus), 下游总线(Secondary bus)和下级总线(Subordinate bus)。上游总线是和 PCI 桥相连 的离CPU较近的总线。下游总线是离 CPU 较远的总线。下级总线则是和 PCI 相连的总线号最大的总线。 对于北桥来说,是没有上游总线的,而他的下游总线就是 pcibus0,因此 pd->bridge.secbus 赋值为 pb。 pb->nextpcimemaddr= pb->minpcimemaddr = PCI_MEM_SPACE_PCI_BASE+0x01000000; PCI_MEM_SPACE_PCI_BASE+BONITO_PCILO_SIZE; pb->minpciioaddr = PCI_IO_SPACE_BASE+0x000a000; pb->nextpciioaddr =PCI_IO_SPACE_BASE+ BONITO_PCIIO_SIZE; pb->pci_mem_base = BONITO_PCILO_BASE_VA; //对应 256M pb->pci_io_base = BONITO_PCIIO_BASE_VA; pb->max_lat = 255; pb->fast_b2b = 1; pb->prefetch = 1; pb->bandwidth = 4000000; pb->ndev = 1; _pci_bushead = pb; _pci_bus[_max_pci_bus++] = pd; Pcibus 的 minimemaddr 被设置 PCI_MEM_SPACE_PCI_BASE+0x01000000。 而 PCI_MEM_SPACE_PCI_BASE 被定义成0,因此这个值就是 16M.之所以这样 做,是因为 ISA 设备只能使用最低 16M内存空间。为了保持兼容性,这里预留 最低 16M 的空间给 ISA。 PCIbus0 的 nextpcimemadr 被设置 PCI_MEM_SPACE_PCI_BASE + BONITO_PCILO_SIZE。这个值是 192M。因此 pci 设备可用的空间就是 16M 到 192M。 Pciioaddr 的值是 PCI_IO_SPACE_BASE+0XA000, Nextioaddr 的值是 PCI_IO_SPACE_BASE+ BONITO_PCIIO_SIZE,也就是 64k。因此 pci 设备可用 的 IO 空间就是从地址 40k 到 64k。 6
bus_dmamap_tag._dmamap_offs = 0; /*set Bonito register*/ BONITO_PCIMAP = BONITO_PCIMAP_WIN(0, PCI_MEM_SPACE_PCI_BASE+0x00000000) | BONITO_PCIMAP_WIN(1, PCI_MEM_SPACE_PCI_BASE+0x04000000) | BONITO_PCIMAP_WIN(2, return(1); PCI_MEM_SPACE_PCI_BASE+0x08000000) | BONITO_PCIMAP_PCIMAP_2; BONITO_PCIBASE0 = PCI_LOCAL_MEM_PCI_BASE; BONITO_PCIBASE1 = PCI_LOCAL_MEM_ISA_BASE; BONITO_PCIBASE2 = PCI_LOCAL_MEM_PCI_BASE + 0x10000000; } 为了说明上面代码,先要讲一下 Bonito 中几个寄存器的含义。 为了让 CPU 访问 PCI 空间,需要将 CPU 空间映射到 PCI 空间。在内存空间 256M 上 方 有 三 个 连 续 的 大 小 均 为 64M 的 区 间 , 分 别 称 为 PCI_Lo0, PCI_Lo1,PCI_Lo2。这三个区间可以被北桥映射到 PCI 以64M对齐的任意位置。 映射的关系通过设置 Bonito 的 PCIMAP 寄存器。该寄存器的格式如下图。 17 12 11 6 5 0 Pci_map2 Pci_lo2 PCIMAP 寄存器 Pci_lo1 Pci_lo0 Pci_lo0,pci_lo1,Pci_lo2 分别是上面所说三个区间的高6位地址(bit31-26),而 Pci_map2 是说明映射到2G以上的空间还是2G 以下的空间。因此上面给 BONITO_PCIMAP 赋值就将 PCI_lo0,PCI_lo1,PCI_lo2 分别映射到了 PCI 空间的 从 0,64M,128M 开始的地址。 另外设备进行 DMA 时,还要提供一种机制将 PCI 地址转换成 CPU 地址。 这 是 通 过 设 置 Base Address Register 。Bonito 中 一 共 有 三 个这 样 的 寄 存 器 pcibas0-2。Pcibas0,pcibase1 都可以映射多达 256M 的空间。具体映射的大小还取 决于 pcimembasecfg 的设置。Pcibase2 是映射 Bonito 的内部寄存器,映射区间为 64k 大小。 寄存器 pcimembasecfg 在 start.S 中设置。我们先看它各个域的含义。 7
Pcimembasecfg 寄存器中的各个域 各个域的含义解释如下: a) Io:说明示映射到 IO 空间还是内存空间。IO=0,则是映射到内存空间, IO=1,则映射的是 IO 空间。 b) Cached:如果设置成1则使用 IOBC,这样可以提高性能 c) Trans/mask 各 5 位。用于决定 pci 地址的 27-23 位。一个 pci 地址,先要 拿掉高三位,然后 mask 取反后和 27-23 位相与,再或上 trans。 在 start.S 中,pcimembasecfg 的 io 位都为 0,cached 为都为 1,因此 pcibase 寄存器映射的是内存空间,并且启用高速缓存。对于 pcibase1,它的 trans=00000b,mask=00000b,这样 pcibase0 映射的空间是 256M,而 pcibase0, 它的 trans=00000b,mask=11111b,这样 pcibase0 映射的空间是 8M,以节省 PCI 空间。 现在再来看上面给 PCIBASE 寄存器赋值的三个语句。PCIBASE0 被赋 值为 PCI_LOCAL_MEM_PCI_BASE(被定义为 0x8000.0000),PCIBASE1 被赋值 PCI_LOCAL_MEM_ISA_BASE(定义为 0x00800000)。这样当 PCI 设备访问地址在 0x8000.0000-0x8FFF.FFFF 时这个地址会先减掉 0x80000000 变成要访问的内存地址。如果设备访问地址在 0x0080.0000-0x008F.FFFF, 经 过 地 址 转 换 后 , 会 变 成 内存 的 低 8M 地 址 。PCIBASE2 被 赋 值 为 PCI_LOCAL_MEM_PCI_BASE + 0x10000000(值为 0x9000.0000)。因此根 据 pci 地址值,能正确的决定要访问的空间。 完成基本的地址映射后,就是初始化 pci 设备了:这包括设备的搜索和 资源的分配。具体的工作在_pci_scan_dev 和_setup_pcibuses 完成的。 _pci_scan_dev 在_pci_businit 中调用。下面是它的调用方式。 for(i = 0, pb = _pci_head; i < pci_roots; i++, pb = pb->next) { 前面已经说过_pci_head 是指向北桥的。Pci_roots 在初始化北桥后被置 //_pci_scan_dev(pb, i, 0, init); _pci_scan_dev(pb, i, 8, init);//from 8+11, hu mingchang } 成 1,因此_pci_scan_dev 在_pci_businit 中只调用一次。 再来看_pci_scan_dev static void _pci_scan_dev(struct pci_device *dev, int bus, int device, int initialise) { { } for(; device < 19; device++) //to 19+11, hu mingchang _pci_query_dev (dev, bus, device, initialise); } 8
分享到:
收藏