设备树寄存器地址不对应?ranges属性分析

树莓派4b,gic地址问题:

interrupt-controller@40041000 {
    interrupt-controller;
    #interrupt-cells = <0x00000003>;
    compatible = "arm,gic-400";
    reg = <0x40041000 0x00001000 0x40042000 0x00002000 0x40044000 0x00002000 0x40046000 0x00002000>;
    interrupts = <0x00000001 0x00000009 0x00000f04>;
    };
但是实际gic物理地址为:0xff841000。所以想看看什么原因导致。

从gic初始化开始查找gic_of_init->gic_of_setup

static int gic_of_setup(struct gic_chip_data *gic, struct device_node *node)
{
	if (!gic || !node)
		return -EINVAL;
	/* 解析gic reg[0] 0x40041000*/
	gic->raw_dist_base = of_iomap(node, 0);
	if (WARN(!gic->raw_dist_base, "unable to map gic dist registers\n"))
		goto error;

	gic->raw_cpu_base = of_iomap(node, 1);
	if (WARN(!gic->raw_cpu_base, "unable to map gic cpu registers\n"))
		goto error;

	if (of_property_read_u32(node, "cpu-offset", &gic->percpu_offset))
		gic->percpu_offset = 0;

	return 0;

error:
	gic_teardown(gic);

	return -ENOMEM;
}

所以需要看看of_iomap的具体实现了。

1、of_iomap
/**
 * of_iomap - Maps the memory mapped IO for a given device_node
 * @device:	the device whose io range will be mapped
 * @index:	index of the io range
 *
 * Returns a pointer to the mapped memory
 */
void __iomem *of_iomap(struct device_node *np, int index)
{
	struct resource res;
	
    /* 获取设备resource信息 */
	if (of_address_to_resource(np, index, &res))
		return NULL;
    
    /* 映射PROT_DEVICE_nGnRE内存信息 */
	return ioremap(res.start, resource_size(&res));
}
struct resource {
	resource_size_t start;
	resource_size_t end;
	const char *name;
	unsigned long flags;
	unsigned long desc;
	struct resource *parent, *sibling, *child;
    ..............
};

#define ioremap(addr, size)		__ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))

整个of_iomap,大致分为两个部分:获取设备树中地址信息;以设备内存方式映射;所以问题原因在获取信息中,继续往下看。

2、of_address_to_resource
int of_address_to_resource(struct device_node *dev, int index,
			   struct resource *r)
{
	const __be32	*addrp;
	u64		size;
	unsigned int	flags;
	const char	*name = NULL;

	addrp = of_get_address(dev, index, &size, &flags);
	if (addrp == NULL)
		return -EINVAL;
		
	/* Get optional "reg-names" property to add a name to a resource */
	of_property_read_string_index(dev, "reg-names",	index, &name);

	return __of_address_to_resource(dev, addrp, size, flags, name, r);
}

其中使用的of_bus的操作集为default的

struct of_bus of_busses[]{
		.name = "default",
		.addresses = "reg",
		.match = NULL,
		.count_cells = of_bus_default_count_cells,
		.map = of_bus_default_map,
		.translate = of_bus_default_translate,
		.get_flags = of_bus_default_get_flags,
	}
  1. of_get_address获取设备树中的节点的reg信息,即获取到0x40041000 0x00001000

2)of_property_read_string_index获取reg-names,没有即为NULL。

  1. __of_address_to_resource关键的转换函数
static int __of_address_to_resource(struct device_node *dev,
		const __be32 *addrp, u64 size, unsigned int flags,
		const char *name, struct resource *r)
{
	u64 taddr;

    /* 默认为IORESOURCE_MEM */
	if (flags & IORESOURCE_MEM)
		taddr = of_translate_address(dev, addrp);
	else if (flags & IORESOURCE_IO)
		taddr = of_translate_ioport(dev, addrp, size);
	else
		return -EINVAL;

	if (taddr == OF_BAD_ADDR)
		return -EINVAL;
	memset(r, 0, sizeof(struct resource));

	r->start = taddr;
	r->end = taddr + size - 1;
	r->flags = flags;
	r->name = name ? name : dev->full_name;
    printk("r->start : 0x%lx \n", taddr);
	return 0;
}
/* addr转换函数 */
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
{
	struct device_node *host;
	u64 ret;
	
    /* 根据prent的ranges转换 */
	ret = __of_translate_address(dev, in_addr, "ranges", &host);
	if (host) {
		of_node_put(host);
		return OF_BAD_ADDR;
	}

	return ret;
}

根据代码后续的查看__of_translate_address->of_translate_one->of_bus_default_translate即发现设备树中ranges的应用原理。

/{
	#address-cells = <2>;
    #size-cells = <1>;
    soc{
    	#address-cells = <1>;
        #size-cells = <1>;
        ranges = <0x7e000000 0x00000000 0xfe000000 0x01800000 
        		  0x7c000000 0x00000000 0xfc000000 0x02000000 
        		  0x40000000 0x00000000 0xff800000 0x00800000>;
        		  
       	interrupt-controller@40041000 {
                        interrupt-controller;
                        #interrupt-cells = <0x00000003>;
                        compatible = "arm,gic-400";
                        reg = <0x40041000 0x00001000 
                        	   0x40042000 0x00002000 
                        	   0x40044000 0x00002000 
                        	   0x40046000 0x00002000>;
                        interrupts = <0x00000001 0x00000009 0x00000f04>;
                };
    }
}

ranges属性值的格式 <local地址parent地址size>, 表示将local地址向parent地址的转换。以gic 0x40041000地址为例,映射gic地址时发现parent节点(/soc)有ranges节点,所以需要转换(若没有就不需要转换)。转换时寻找适合的ranges,通过匹配条件local地址<addr<local地址+size,如若当前适合的最后一个ranges满足0x40000000<0x40041000<0x40000000+0x00800000。找到后进行转换:

addr = 0x40041000;
ranges = <0x40000000 0x00000000 0xff800000 0x00800000>;
local_addr = 0x40000000;
parent_addr = 0x00000000ff800000;
size = 0x00800000;
taddr = (addr - local_addr) + parent_addr;
	  = 0xff841000;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值