当前位置: 首页 > news >正文

linux-5.10.110内核源码分析 - bcm2711 pcie BAR地址分配

1、pcie地址(bcm2711 Address Maps)

1.1、pcie地址(Address Maps)

1.2、dts(ranges)

pcie0: pcie@7d500000 {
        compatible = "brcm,bcm2711-pcie";
        reg = <0x0 0x7d500000  0x0 0x9310>;
        device_type = "pci";
        #address-cells = <3>;
        #interrupt-cells = <1>;
        #size-cells = <2>;
        interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
                     <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
        interrupt-names = "pcie", "msi";
        interrupt-map-mask = <0x0 0x0 0x0 0x7>;
        interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143
                                        IRQ_TYPE_LEVEL_HIGH>,
                        <0 0 0 2 &gicv2 GIC_SPI 144
                                        IRQ_TYPE_LEVEL_HIGH>,
                        <0 0 0 3 &gicv2 GIC_SPI 145
                                        IRQ_TYPE_LEVEL_HIGH>,
                        <0 0 0 4 &gicv2 GIC_SPI 146
                                        IRQ_TYPE_LEVEL_HIGH>;
        msi-controller;
        msi-parent = <&pcie0>;

        ranges = <0x02000000 0x0 0xc0000000 0x6 0x00000000
                  0x0 0x40000000>;
        /*
         * The wrapper around the PCIe block has a bug
         * preventing it from accessing beyond the first 3GB of
         * memory.
         */
        dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000
                      0x0 0xc0000000>;
        brcm,enable-ssc;
};

从dts及bcm2711 Address Maps可以看到,pcie的cpu域地址为0x0600000000-0x063fffffff,大小为0x40000000。

2、pcie初始化(brcm_pcie_probe)

2.1、添加资源到pci_host_bridge->windows(pci_add_resource_offset)

        对于当前调用栈,pci_add_resource_offset函数的resources参数即为pci_host_bridge->windows,参数res即为dts里面描述的ranges的cpu域地址范围0x0600000000-0x063fffffff以及其他数据,参数offset即为cpu域地址到pci地址的偏移(dts里面cpu域地址-pci域地址)。

2.2、添加资源到pci_bus->resources(pci_bus_add_resource)

2.3、ep配置空间映射(brcm_pcie_map_conf)

        配置空间映射(brcm_pcie_map_conf),bcm2711所有设备配置空间的cpu域地址都是一个地址,通过写PCIE_EXT_CFG_INDEX索引寄存器来区分对不同设备配置空间的读写:

2.4、BAR0资源读取计算(__pci_read_base)

        __pci_read_base读取计算BAR0的资源:

        计算方法参考上图,计算过程大致可以解释为向BAR写全1,其中上图中的4-25位固定为0,写1之后仍然为0,0-3也是固定的,写1保持不不变;

        pci_size的:

u64 size = mask & maxbase;	/* Find the significant bits */

        是将0-3位清0,清0之后,size的高位都为1,低位都为0。

        pcie_size的:

size = size & ~(size-1);

        “(size-1)”因为size的低位都为0,都需要向前借1,借1减1之后0变成1,也就是上图的0-25位都变成1,因为25位需要向26位借1,所以26位变成0,最终就是26位之外的二进制位都为1,“~(size-1)”取反之后除26位之外为1,其他位都为0,“size & ~(size-1)”最终结果还是第26位为1,也就是BAR大小,2的26次方。

3、BAR资源分配(pci_bus_alloc_resource)

3.1、资源管理方式(resource)

        BAR资源管理大致如下所示,已分配的资源链接到child,如果资源没有分配那么child为空,child、sibling之间按地址从小到大链接在一起,child及sibling都是已经分配了的资源,child、sibling及sibling、sibling之间的空隙为没有分配的空闲资源(BAR资源要求对齐,对齐之后BAR资源之间有空隙),child之前及最后一个sibling之后的资源为空闲资源:

3.2、查找空闲资源(__find_resource)

        查找空闲资源主要过程就是依次在最前面的空闲资源、已分配的资源节点之间、最后空闲资源里

面查找符合要求的资源区间,然后返回该区间,这里没有分配资源,仅返回空闲区间:

/*
 * Find empty slot in the resource tree with the given range and
 * alignment constraints
 */
static int __find_resource(struct resource *root, struct resource *old,
			 struct resource *new,
			 resource_size_t  size,
			 struct resource_constraint *constraint)
{
	struct resource *this = root->child; // 已分配的资源链表
	struct resource tmp = *new, avail, alloc;

	tmp.start = root->start; // 从root->start开始查找空闲资源
	/*
	 * Skip past an allocated resource that starts at 0, since the assignment
	 * of this->start - 1 to tmp->end below would cause an underflow.
	 */
	if (this && this->start == root->start) { // 已经有分配资源,并且root->start已经被分配出去,那么从root->child之后开始查找空闲资源
		tmp.start = (this == old) ? old->start : this->end + 1; // old有值为重新分配内存的情况,这里不考虑,从this->end + 1开始查找空闲资源,也就是root->child之后
		this = this->sibling; // this指向下一个已分配的资源节点(tmp是介于两个已分配资源节点之间的空闲资源)
	}
	for(;;) {
		if (this)
			tmp.end = (this == old) ?  this->end : this->start - 1; // tmp的结束地址指向下一个已分配资源的开始地址的前一个地址,空闲资源的结束地址
		else
			tmp.end = root->end; // tmp之后没有已经分配的资源节点,tmp到资源的最后都是空闲资源

		if (tmp.end < tmp.start) // 前后两个节点之间没有空闲资源的情况(两个资源节点的内存紧挨着)
			goto next;

		resource_clip(&tmp, constraint->min, constraint->max);
		arch_remove_reservations(&tmp);

		/* Check for overflow after ALIGN() */
		avail.start = ALIGN(tmp.start, constraint->align); // 起始地址对齐(对齐之后的地址作为空闲资源的起始地址)
		avail.end = tmp.end; // 可用资源的结束地址(tmp.end之后已经被分配了)
		avail.flags = new->flags & ~IORESOURCE_UNSET;
		if (avail.start >= tmp.start) { // 对齐之后的地址大于等于tmp.start(对齐之后的地址在空闲资源范围内,而不是与前一个已经分配的资源节点的地址重叠冲突)
			alloc.flags = avail.flags;
			alloc.start = constraint->alignf(constraint->alignf_data, &avail,
					size, constraint->align); // 根据pcie硬件参数等,对分配的资源地址进行对齐
			alloc.end = alloc.start + size - 1; // 起始地址对齐之后,更新结束地址
			if (alloc.start <= alloc.end && // 没看到什么情况会不成立,除非size小于等于0
			    resource_contains(&avail, &alloc)) { // alloc的地址在可用资源地址范围之内,那么alloc作为查找到的可用资源保存到new,这个时候只是返回一个可用的范围,没有真正申请资源
				new->start = alloc.start;
				new->end = alloc.end;
				return 0;
			}
		}

next:		if (!this || this->end == root->end)
			break;

		if (this != old) // (对于非重新分配内存,需要更新tmp.start,对于重新分配资源,如果this是旧的节点,那么可以把旧的节点再次分配给新的资源)
			tmp.start = this->end + 1; // tmp.start更新为当前sibling结束地址的下一个地址
		this = this->sibling; // sibling跟前一个节点之间的空闲资源不满足需要申请的资源,查找当前sibling及下一个sibling之间的空闲资源
	}
	return -EBUSY;
}

3.3、申请空闲资源(__request_resource)

        __request_resource申请空闲资源比较简单,主要就是将前面查找到的空闲资源按起始地址大小插入到pci_bus->resources->child已分配资源链表:

/* Return the conflict entry if you can't request it */
static struct resource * __request_resource(struct resource *root, struct resource *new)
{
	resource_size_t start = new->start; // 新申请资源的起始地址
	resource_size_t end = new->end; // 新申请资源的结束地址
	struct resource *tmp, **p;

	if (end < start)
		return root;
	if (start < root->start)
		return root;
	if (end > root->end)
		return root;
	p = &root->child; // 已分配资源的第一个节点
	for (;;) {
		tmp = *p; // root->child或者tmp->sibling
		if (!tmp || tmp->start > end) { // 第一次循环,如果tmp为空,那么资源没有分配,new就是第一个分配的资源节点,root->child指向new即可,非第一次循环tmp为空,表示已经查找到已经分配资源节点的最后一个节点的下一个节点,也就是NULL,那么new就是最后一个节点
			new->sibling = tmp; // new节点的下一个节点(new是最后一个节点,那么tmp为NULL,如果new不是最后一个节点,那么tmp是new的下一个节点)
			*p = new; // 第一次分配内存资源的情况下,root->child = new;非第一次内存分配,new插入p节点后
			new->parent = root;
			return NULL;
		}
		p = &tmp->sibling; // 保存前一个节点的sibling,用于记录new的前区节点
		if (tmp->end < start)
			continue;
		return tmp;
	}
}

4、写BAR寄存器(pci_std_update_resource)

4.1、cpu域转pci域地址(pcibios_resource_to_bus)

        资源里面分配的地址是cpu域地址,BAR里面保存的是pci域地址,需要找到资源在哪个resource里面,找到pci域到cpu域的地址偏移,cpu域地址减去一个偏移才是BAR寄存器要写入的值,res是cpu域地址,region是最终的pci域地址:

4.2、更新BAR(pci_std_update_resource)

相关文章:

  • 牛客寒假训练营3
  • 芯片引脚描述或电路原理图中的Ipd、Ipu是什么意思?
  • fps武器系统6:随机弹道
  • vLLM专题(四)-故障排除
  • vue中如何动态的增减组件的类名(class)
  • OpenPose
  • 计算机世界的寻宝游戏:用C语言解密五大查找算法
  • 数据仓库与数据湖的协同工作:智慧数据管理的双引擎
  • 类和对象(5)——抽象类和接口
  • 云存储:云计算储存
  • 安卓设备调试h5页面(调试)
  • 量化交易入门指南
  • 《Stable Diffusion绘画完全指南:从入门到精通的Prompt设计艺术》-配套代码示例
  • 位运算,双指针,二分,排序算法
  • WeMos D1+PIR+Android 的小场景制作
  • freertos源码分析DAY12 (软件定时器)
  • 【第14章:神经符号集成与可解释AI—14.1 神经符号AI系统的基本原理与实现方法】
  • 一款简单的弹窗打赏页HTML源码
  • python入门详解
  • EasyRTC智能硬件:小体积,大能量,开启音视频互动新体验
  • 河南信阳拟发文严控预售许可条件:新出让土地开发的商品房一律现房销售
  • 飙升至熔断,巴基斯坦股市两大股指收盘涨逾9%
  • 普京提议恢复直接谈判,泽连斯基:望俄明日停火,乌愿谈判
  • 玉渊谭天丨中方为何此时同意与美方接触?出于这三个考虑
  • 方正证券总裁何亚刚到龄退休,54岁副总裁姜志军接棒
  • 国办印发《关于进一步加强困境儿童福利保障工作的意见》