linux 设备树-of_address_to_resource

实例分析-reg 属性解析(基于ranges属性)

实例1

/{
	#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>;
			......
		};
	}
	......
}

 实例2

 

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;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
devm_ioremap() is a function used in Linux kernel programming to map a physical memory region into the kernel's virtual address space. It is a variant of the ioremap() function that is designed to be used with managed devices in the kernel. The devm prefix stands for device-managed, which means that the mapping is automatically released when the device is removed or the driver module is unloaded. This avoids the need for explicit cleanup code in the driver and reduces the risk of resource leaks. The function takes three arguments: the device pointer, the physical address of the memory region to map, and the size of the region. It returns a virtual address that can be used to access the memory region from kernel code. Here is an example usage of devm_ioremap(): ``` static int my_driver_probe(struct platform_device *pdev) { void __iomem *base_addr; int ret; base_addr = devm_ioremap(&pdev->dev, PHYS_ADDR, SIZE); if (!base_addr) { dev_err(&pdev->dev, "Failed to map memory region\n"); return -ENOMEM; } // Use base_addr to access the memory region return 0; } ``` In this example, the driver is using devm_ioremap() to map a physical memory region defined by the PHYS_ADDR and SIZE constants. If the mapping fails, an error message is printed and the probe function returns an error code. Otherwise, the driver can use the base_addr pointer to access the memory region. When the device is removed or the driver module is unloaded, the memory mapping will be automatically released by the kernel.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐分享-程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值