实例分析-reg 属性解析(基于ranges属性)
/{
#address-cells = <0x01>;
#size-cells = <0x01>;
soc {
compatible = "simple-bus";
#address-cells = <0x01>;
#size-cells = <0x01>;
ranges = <0x7e000000 0x3f000000 0x1000000 0x40000000 0x40000000 0x1000>;
dma@7e007000 {
compatible = "brcm,bcm2835-dma";
reg = <0x7e007000 0xf00>;
......
};
}
......
}
struct resource {
resource_size_t start; // 0x3f007000
resource_size_t end; // 0x3f007eff
const char *name; // dma@7e007000 (reg-names 或者device_node->full_name)
unsigned long flags; // 0x200 IORESOURCE_MEM
unsigned long desc;
struct resource *parent, *sibling, *child;
};
1. of_address_to_resource 整体分析
of_address_to_resource(np, num_reg, &temp_res)
分析:获取节点的reg 属性,生成struct resource
最终填充到platfor_device 结构体的pdev->num_resources和 pdev->resource
核心的三步操作:
1. addrp = of_get_address(dev, index, &size, &flags);
分析: 获取reg 属性的 第index组的信息(addrp:基地址 size: 大小 flags:默认)
2. of_property_read_string_index(dev, "reg-names",index, &name);
分析: 从reg-names 属性中获取index组的名称
3. return __of_address_to_resource(dev, addrp, size, flags, name, r);
分析: 进行地址转换(基于父节点的ranges属性),并存储到struct resource *r 结构体中
第一步: of_get_address
const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
unsigned int *flags)
{
const __be32 *prop;
unsigned int psize;
struct device_node *parent;
struct of_bus *bus;
int onesize, i, na, ns;
/* Get parent & match bus type */
parent = of_get_parent(dev);
if (parent == NULL)
return NULL;
bus = of_match_bus(parent);
// 获取 of_bus 结构体:static struct of_bus of_busses[]
// 匹配方式:np->type 或者 np->name
bus->count_cells(dev, &na, &ns);
// 提取父节点或更高层节点的#address-cells和 #size-cells
of_node_put(parent);
if (!OF_CHECK_ADDR_COUNT(na))
return NULL;
/* Get "reg" or "assigned-addresses" property */
prop = of_get_property(dev, bus->addresses, &psize); //prop 是基地址,
//psize 是字节流的长度,
if (prop == NULL)
return NULL;
psize /= 4; // psize/4 是cell 的长度
onesize = na + ns; //一组 (addr size) 的长度
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
if (i == index) {
if (size)
*size = of_read_number(prop + na, ns);
if (flags)
*flags = bus->get_flags(prop);
return prop;
}
return NULL;
}
第二步:of_property_read_string_index()
略
第三步:__of_address_to_resource()
__of_address_to_resource(dev, addrp, size, flags, name, r);
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;
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; //填充到struct resource 结构体中
r->end = taddr + size - 1;
r->flags = flags;
r->name = name ? name : dev->full_name;
return 0;
}
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
{
struct device_node *host;
u64 ret;
ret = __of_translate_address(dev, in_addr, "ranges", &host); 根据父节点的ranges 进行多次转换(比如父节点的父节点)
if (host) {
of_node_put(host);
return OF_BAD_ADDR;
}
return ret;
}
static u64 __of_translate_address(struct device_node *dev,
const __be32 *in_addr, const char *rprop,
struct device_node **host)
{
struct device_node *parent = NULL;
struct of_bus *bus, *pbus;
__be32 addr[OF_MAX_ADDR_CELLS];
int na, ns, pna, pns;
u64 result = OF_BAD_ADDR;
pr_debug("** translation for device %pOF **\n", dev);
/* Increase refcount at current level */
of_node_get(dev);
*host = NULL;
/* Get parent & match bus type */
parent = of_get_parent(dev);
if (parent == NULL)
goto bail;
bus = of_match_bus(parent);
/* Count address cells & copy address locally */
bus->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
pr_debug("Bad cell count for %pOF\n", dev);
goto bail;
}
memcpy(addr, in_addr, na * 4);
pr_debug("bus is %s (na=%d, ns=%d) on %pOF\n",
bus->name, na, ns, parent);
of_dump_addr("translating address:", addr, na);
/* Translate */
for (;;) {
struct logic_pio_hwaddr *iorange;
/* Switch to parent bus */
of_node_put(dev);
dev = parent;
parent = of_get_parent(dev);
/* If root, we have finished */
if (parent == NULL) {
pr_debug("reached root node\n");
result = of_read_number(addr, na); //最终,根据addr这个地址 生成整数,然后返回整数
break;
}
/*
* For indirectIO device which has no ranges property, get
* the address from reg directly.
*/
iorange = find_io_range_by_fwnode(&dev->fwnode);
if (iorange && (iorange->flags != LOGIC_PIO_CPU_MMIO)) {
result = of_read_number(addr + 1, na - 1);
pr_debug("indirectIO matched(%pOF) 0x%llx\n",
dev, result);
*host = of_node_get(dev);
break;
}
/* Get new parent bus and counts */
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
pr_err("Bad cell count for %pOF\n", dev);
break;
}
pr_debug("parent bus is %s (na=%d, ns=%d) on %pOF\n",
pbus->name, pna, pns, parent);
/* Apply bus translation */
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
break;
/* Complete the move up one level */
na = pna;
ns = pns;
bus = pbus;
of_dump_addr("one level translation:", addr, na);
}
bail:
of_node_put(parent);
of_node_put(dev);
return result;
}
static int of_translate_one(struct device_node *parent, struct of_bus *bus,
struct of_bus *pbus, __be32 *addr,
int na, int ns, int pna, const char *rprop)
{
const __be32 *ranges;
unsigned int rlen;
int rone;
u64 offset = OF_BAD_ADDR;
/*
* Normally, an absence of a "ranges" property means we are
* crossing a non-translatable boundary, and thus the addresses
* below the current cannot be converted to CPU physical ones.
* Unfortunately, while this is very clear in the spec, it's not
* what Apple understood, and they do have things like /uni-n or
* /ht nodes with no "ranges" property and a lot of perfectly
* useable mapped devices below them. Thus we treat the absence of
* "ranges" as equivalent to an empty "ranges" property which means
* a 1:1 translation at that level. It's up to the caller not to try
* to translate addresses that aren't supposed to be translated in
* the first place. --BenH.
*
* As far as we know, this damage only exists on Apple machines, so
* This code is only enabled on powerpc. --gcl
*/
ranges = of_get_property(parent, rprop, &rlen);
if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
pr_debug("no ranges; cannot translate\n");
return 1;
}
if (ranges == NULL || rlen == 0) {
offset = of_read_number(addr, na);
memset(addr, 0, pna * 4);
pr_debug("empty ranges; 1:1 translation\n");
goto finish;
}
pr_debug("walking ranges...\n");
/* Now walk through the ranges */
// ranges 中存在多组转换, 选择正确的转换(ranges 地址为正确的一组的基地址)
//offset = da- cp
rlen /= 4;
rone = na + pna + ns;
for (; rlen >= rone; rlen -= rone, ranges += rone) {
offset = bus->map(addr, ranges, na, ns, pna);
if (offset != OF_BAD_ADDR)
break;
}
if (offset == OF_BAD_ADDR) {
pr_debug("not found !\n");
return 1;
}
memcpy(addr, ranges + na, 4 * pna); //addr= 转换后的基地址
finish:
of_dump_addr("parent translation for:", addr, pna);
pr_debug("with offset: %llx\n", (unsigned long long)offset);
/* Translate it into parent bus space */
return pbus->translate(addr, offset, pna); // addr = addr + offset
}
bus->map(addr, ranges, na, ns, pna);
调用 of_bus_default_map
获取偏移(用于转换后的偏移)
static u64 of_bus_default_map(__be32 *addr, const __be32 *range,
int na, int ns, int pna)
{
u64 cp, s, da;
cp = of_read_number(range, na); 获取ranges 中的基地址(未转换的)
s = of_read_number(range + na + pna, ns); 获取ranges 中基地址的大小
da = of_read_number(addr, na);//获取reg 中的基地址(未转换的)
pr_debug("default map, cp=%llx, s=%llx, da=%llx\n",
(unsigned long long)cp, (unsigned long long)s,
(unsigned long long)da);
if (da < cp || da >= (cp + s)) //有效: 必须大于 cp, 且小于 cp +s
return OF_BAD_ADDR;
return da - cp; // 计算偏移(未转换前的)
}
pbus->translate(addr, offset, pna);
调用of_bus_default_translate
static int of_bus_default_translate(__be32 *addr, u64 offset, int na)
{
u64 a = of_read_number(addr, na);
memset(addr, 0, na * 4);
a += offset;
if (na > 1)
addr[na - 2] = cpu_to_be32(a >> 32);
addr[na - 1] = cpu_to_be32(a & 0xffffffffu);
return 0;
}