树莓派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,
}
- of_get_address获取设备树中的节点的reg信息,即获取到
0x40041000 0x00001000
2)of_property_read_string_index获取reg-names
,没有即为NULL。
- __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;