platform_get_resource获取设备树资源出现失败,确保参数没有错误,且设备树中确实有对应device,举例如下:
代码如下
设备树内容如下
通过代码我们知当没有获取到资源的时候,就会报错。
1.第一步,我们看一下什么情况下platform_get_resource函才会返回NULL,也就是没有获取到资源。
platform_get_resource函数定又在drivers/base/platform.c中
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num) { int i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; if (type == resource_type(r) && num-- == 0) return r; } return NULL; }
说明根本没有进入for循环,为什么?也就是说dev->num_resources为0,为什么为0?正常的话dev->num_resources应该为1。
通过代码我们可知,当for循环不成立的时候,也就是为时,才会返回NULL。
所以我们要分析为什么dev->num_resources为0?
通过之前的内容我们知道设备树最终转换成platform_device(并不是所有节点都会转),在转成platform_device的时候,会对num_resources进行值,所以跟踪下设备的转换流程,看一下什么情况下会被值成0。
2.第二步,因为myled这个设节点会转成了platform_device,所以我们只需要分忻构建resources赋值的代码即可。
通过的面的学习我们知道:
在drivers/of/platform.c下的of_platform_device_create_pdata下的of_device_alloc函数会创建
resources,
struct platform_device *of_device_alloc(struct device_node *np, const char *bus_id, struct device *parent) { struct platform_device *dev; int rc, i, num_reg = 0, num_irq; struct resource *res, temp_res; dev = platform_device_alloc("", -1); if (!dev) return NULL; /* count the io and irq resources */ while (of_address_to_resource(np, num_reg, &temp_res) == 0) num_reg++; num_irq = of_irq_count(np); /* Populate the resource table */ if (num_irq || num_reg) { res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); if (!res) { platform_device_put(dev); return NULL; } dev->num_resources = num_reg + num_irq; dev->resource = res; for (i = 0; i < num_reg; i++, res++) { rc = of_address_to_resource(np, i, res); WARN_ON(rc); } if (of_irq_to_resource_table(np, res, num_irq) != num_irq) pr_debug("not all legacy IRQ resources mapped for %s\n", np->name); } dev->dev.of_node = of_node_get(np); dev->dev.parent = parent ? : &platform_bus; if (bus_id) dev_set_name(&dev->dev, "%s", bus_id); else of_device_make_bus_id(&dev->dev); return dev; }
dev->num_resources = num_reg + num_irq;
节点中没有中断资源,所以num_irq为0,num_reg初始化是0,下面代码统计num_reg数量
/* count the io and irq resources */
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
num_reg++;
of_device_alloc
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); }
EXPORT_SYMBOL_GPL(of_address_to_resource);
of_device_alloc
of_address_to_resource
__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; if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) return -EINVAL; taddr = of_translate_address(dev, addrp); if (taddr == OF_BAD_ADDR) return -EINVAL; memset(r, 0, sizeof(struct resource)); if (flags & IORESOURCE_IO) { unsigned long port; port = pci_address_to_pio(taddr); if (port == (unsigned long)-1) return -EINVAL; r->start = port; r->end = port + size - 1; } else { r->start = taddr; r->end = taddr + size - 1; } r->flags = flags; r->name = name ? name : dev->full_name; return 0; }
taddr = of_translate_address(dev, addrp);
of_device_alloc
of_address_to_resource
__of_address_to_resource
of_translate_address(传入了ranges属性)
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) { return __of_translate_address(dev, in_addr, "ranges"); } EXPORT_SYMBOL(of_translate_address);
__of_translate_address
static u64 __of_translate_address(struct device_node *dev, const __be32 *in_addr, const char *rprop) { 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("OF: ** translation for device %s **\n", of_node_full_name(dev)); /* Increase refcount at current level */ of_node_get(dev); /* 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("OF: Bad cell count for %s\n", of_node_full_name(dev)); goto bail; } memcpy(addr, in_addr, na * 4); pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n", bus->name, na, ns, of_node_full_name(parent)); of_dump_addr("OF: translating address:", addr, na); /* Translate */ for (;;) { /* 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("OF: reached root node\n"); result = of_read_number(addr, na); break; } /* Get new parent bus and counts */ pbus = of_match_bus(parent); pbus->count_cells(dev, &pna, &pns); if (!OF_CHECK_COUNTS(pna, pns)) { printk(KERN_ERR "prom_parse: Bad cell count for %s\n", of_node_full_name(dev)); break; } pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n", pbus->name, pna, pns, of_node_full_name(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("OF: one level translation:", addr, na); } bail: of_node_put(parent); of_node_put(dev); return result; }
/* Apply bus translation */ if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) break;
注意:of_translate_one参数rprop为“ranges"
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 not 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("OF: 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("OF: empty ranges; 1:1 translation\n"); goto finish; } pr_debug("OF: walking ranges...\n"); /* Now walk through the ranges */ 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("OF: not found !\n"); return 1; } memcpy(addr, ranges + na, 4 * pna); finish: of_dump_addr("OF: parent translation for:", addr, pna); pr_debug("OF: with offset: %llx\n", (unsigned long long)offset); /* Translate it into parent bus space */ return pbus->translate(addr, offset, pna); }
由于属性中没有ranges属性,所以ranges=NULL
ranges = of_get_property(parent, rprop, &rlen); if (ranges == NULL && !of_empty_ranges_quirk(parent)) { pr_debug("OF: no ranges; cannot translate\n"); return 1;
导致of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)=1
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)) break;
导致函数直接返回result
return result;
而result的值如下
u64 result = OF_BAD_ADDR;
所以
u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr) { return __of_translate_address(dev, in_addr, "dma-ranges"); }
返回OF_BAD_ADDR
taddr = of_translate_address(dev, addrp);
if (taddr == OF_BAD_ADDR)
return -EINVAL;
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); }
返回-EINVAL
while (of_address_to_resource(np, num_reg, &temp_res) == 0) num_reg++;
所以不会进入while循环,也就是说num_reg=0;所以num_resources=0;
总结
of_translateone返回1
of_translate_address回OFBAD_ADDR
ofaddresstoresource返回EINVAL
ofaddresstoresource返回EINVAL
所以numreg为0,
通过代码的分折,当有ranges属性的时,但是属性值为0,则不对地址进行转,所以在设备节点中添加ranges属性即可。
如果文章对您有帮助,点赞👍支持,感谢🤝