platform_get_resource怎么获取reg和irq的

platform_get_resource_byname

下面是一段那paltform_device获取irq和reg的代码,platform_get_resource_byname加IORESOURCE_MEM参数获取特定名字的内存空间

static int xxx_pcie_parse_port(struct falcon_pcie *pcie,
                               struct device_node *node,
                               int slot)
{
        struct falcon_pcie_port *port;
        struct resource *regs;
        struct device *dev = pcie->dev;
        struct platform_device *pdev = to_platform_device(dev);
        const __be32 *prop;
        unsigned int proplen;
        int err;

        port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
        if (!port)
                return -ENOMEM;

        regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pciephy");
        port->phy_base = (void *)(regs->start);

        regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pciectrl");
        port->base = devm_ioremap_resource(dev, regs);
        if (IS_ERR(port->base)) {
                dev_err(dev, "failed to map port%d base\n", slot);
                return PTR_ERR(port->base);
        }

        port->axi_ck = of_clk_get_by_name(node, NULL);
        if (IS_ERR(port->axi_ck)) {
                dev_err(&pdev->dev, "Error %ld to get pcie clock\n",
                        PTR_ERR(port->axi_ck));
                return PTR_ERR(port->axi_ck);
        }
		/* some platforms may use default PHY setting */
        port->phy = devm_phy_optional_get(dev, "pcie-phy");
        if (IS_ERR(port->phy))
                return PTR_ERR(port->phy);

        port->slot = slot;
        port->pcie = pcie;
        pcie->port = port;
        mutex_unlock(&port->lock);

        prop = of_get_property(node, "lpm-qos", &proplen);
        if (prop)
                port->lpm_qos = be32_to_cpup(prop);

        err = of_property_read_u32(node, "interrupts", &(port->irq));
        if (err)
                return err;

        port->irq += 16;
        err = devm_request_irq(dev, port->irq, falcon_pcie_irq_handler,
                                IRQF_SHARED, "falcon-pcie", port);
        if (err) {
                dev_err(dev, "failed to request irq %d\n", port->irq);
                return err;
        }
        port->gpio_reset = of_get_named_gpio(node, "reset-gpios", 0);
        if (port->gpio_reset < 0)
                return -ENODEV;

		INIT_LIST_HEAD(&port->list);

        return 0;
}

上面通过直接读中断属性,读出的物理中断号值加上16得出最终的irq;platform_get_irq会调用platform_get_resource来获取irq;那证明在platform_get_resource之前已经有过转换操作了,也即of_irq_get

int platform_get_irq(struct platform_device *dev, unsigned int num)
{
#ifdef CONFIG_SPARC
	/* sparc does not have irqs represented as IORESOURCE_IRQ resources */
	if (!dev || num >= dev->archdata.num_irqs)
		return -ENXIO;
	return dev->archdata.irqs[num];
#else
	struct resource *r;
	if (IS_ENABLED(CONFIG_OF_IRQ) && dev->dev.of_node) {
		int ret;

		ret = of_irq_get(dev->dev.of_node, num);
		if (ret > 0 || ret == -EPROBE_DEFER)
			return ret;
	}

	r = platform_get_resource(dev, IORESOURCE_IRQ, num);
	if (has_acpi_companion(&dev->dev)) {
		if (r && r->flags & IORESOURCE_DISABLED) {
			int ret;

			ret = acpi_irq_get(ACPI_HANDLE(&dev->dev), num, r);
			if (ret)
				return ret;
		}
	}

	/*
	 * The resources may pass trigger flags to the irqs that need
	 * to be set up. It so happens that the trigger flags for
	 * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER*
	 * settings.
	 */
	if (r && r->flags & IORESOURCE_BITS) {
		struct irq_data *irqd;

		irqd = irq_get_irq_data(r->start);
		if (!irqd)
			return -ENXIO;
		irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS);
	}

	return r ? r->start : -ENXIO;
#endif
}

早期解析设备树

实际内核启动早期解析设备树,创建platform_device的时候,就把reg和irq单独做处理了,reg是直接获取的,irq通过of_irq_get来完成映射和转换了

of_platform_bus_create
    of_platform_device_create_pdata
        of_device_alloc

of_device_alloc{
    ....
    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);
    ....
}

reg

获取reg信息,并通过__of_address_to_resource放进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);

irq

获取多个irq信息

int of_irq_to_resource_table(struct device_node *dev, struct resource *res,
		int nr_irqs)
{
	int i;

	for (i = 0; i < nr_irqs; i++, res++)
		if (of_irq_to_resource(dev, i, res) <= 0)
			break;

	return i;
}

of_irq_get去隐射和转换出irq ,并将resource的start和end都设置成irq

int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
{
	int irq = of_irq_get(dev, index);

	if (irq < 0)
		return irq;

	/* Only dereference the resource if both the
	 * resource and the irq are valid. */
	if (r && irq) {
		const char *name = NULL;

		memset(r, 0, sizeof(*r));
		/*
		 * Get optional "interrupt-names" property to add a name
		 * to the resource.
		 */
		of_property_read_string_index(dev, "interrupt-names", index,
					      &name);

		r->start = r->end = irq;
		r->flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));
		r->name = name ? name : of_node_full_name(dev);
	}

	return irq;
}

pcie的range获取

设备树的pcie节点的ranges信息如下

pcie0: pcie@0xd4288000{
		......
		device_type = "pci";
		#address-cells = <3>;
		#size-cells = <2>;
		bus-range = <0x00 0xff>;
		linux,pci-domain = <0>;
		reg = <0xd4210000 0x800>,       /* PCIe PHY registers */
					<0xd4288000 0x1000>;  /* PCIe config space */
		reg-names = "pciephy", "pciectrl";
		phys = <&pcieport0 0>;
		phy-names = "pcie-phy";
		ranges =  <0x81000000 0 0 0xE0010000 0 0x00010000                       /* downstream I/O */
						0x82000000 0 0xE0020000 0xE0020000 0 0x04000000>;       /* memory */
		lpm-qos = <PM_QOS_CPUIDLE_BLOCK_AXI>;
		num-lanes = <1>;
		interrupts = <18>;
		#interrupt-cells = <1>;
		interrupt-parent = <&intc>;
		interrupt-map-mask = <0 0 0 0>;
		interrupt-map = <0 0 0 0 &intc 18>;
		clocks = <&soc_clocks ASR1803_CLK_PCIE0>;
		status = "disabled";
};

pcie主机驱动从设备树获取I/O空间和内存空间

int __init xxx_pcie_host_init(struct falcon_pcie_port *pp)
{
        struct device_node *np = pp->dev->of_node;
        struct of_pci_range range;
        struct of_pci_range_parser parser;
        int i, err;

        if (of_pci_range_parser_init(&parser, np)) {
                dev_err(pp->dev, "missing ranges property\n");
                return -EINVAL;
        }

        /* Get the I/O and memory ranges from DT */
        for_each_of_pci_range(&parser, &range) {
                unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
                if (restype == IORESOURCE_IO) {
                        of_pci_range_to_resource(&range, np, &pp->io);
                        pp->io.name = "I/O";
                        pp->io.start = max_t(resource_size_t,
                                             PCIBIOS_MIN_IO,
                                             range.pci_addr + global_io_offset);
                        pp->io.end = min_t(resource_size_t,
                                           IO_SPACE_LIMIT,
                                           range.pci_addr + range.size
                                           + global_io_offset);
                        pp->config.io_size = resource_size(&pp->io);
                        pp->config.io_bus_addr = range.pci_addr;
                }
                if (restype == IORESOURCE_MEM) {
                        of_pci_range_to_resource(&range, np, &pp->mem);
                        pp->mem.name = "MEM";
                        pp->config.mem_size = resource_size(&pp->mem);
                        pp->config.mem_bus_addr = range.pci_addr;
                }
                if (restype == 0) {
                        of_pci_range_to_resource(&range, np, &pp->cfg);
                        pp->config.cfg0_size = resource_size(&pp->cfg)/2;
                        pp->config.cfg1_size = resource_size(&pp->cfg)/2;
                }
        }

        pp->mem_base = pp->mem.start;
        if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
                dev_err(pp->dev, "Failed to parse the number of lanes\n");
                return -EINVAL;
        }
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
                                        MAX_MSI_IRQS, &msi_domain_ops,
                                        &falcon_msi_irq_chip);
                if (!pp->irq_domain) {
                        dev_err(pp->dev, "irq domain init failed\n");
                        return -ENXIO;
                }

                for (i = 0; i < MAX_MSI_IRQS; i++) {
                        irq_create_mapping(pp->irq_domain, i);
                }
        }

        err = xr3pci_setup(pp, pp->dev);
        if (err < 0)
                return err;

        pp->hw_init = 0;
        return 0;
}

of_pci_range_parser_init获取ranges属性

int of_pci_range_parser_init(struct of_pci_range_parser *parser,
				struct device_node *node)
{
	const int na = 3, ns = 2;
	int rlen;

	parser->node = node;
	parser->pna = of_n_addr_cells(node);
	parser->np = parser->pna + na + ns;

	parser->range = of_get_property(node, "ranges", &rlen);
	if (parser->range == NULL)
		return -ENOENT;

	parser->end = parser->range + rlen / sizeof(__be32);

	return 0;
}

这个遍历函数会去解析每一个range的flags,pci_addr,cpu_addr,size等信息

#define for_each_of_pci_range(parser, range) \
	for (; of_pci_range_parser_one(parser, range);)

struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
						struct of_pci_range *range)
{
	const int na = 3, ns = 2;

	if (!range)
		return NULL;

	if (!parser->range || parser->range + parser->np > parser->end)
		return NULL;

	range->pci_space = be32_to_cpup(parser->range);
	range->flags = of_bus_pci_get_flags(parser->range);
	range->pci_addr = of_read_number(parser->range + 1, ns);
	range->cpu_addr = of_translate_address(parser->node,
				parser->range + na);
	range->size = of_read_number(parser->range + parser->pna + na, ns);

	parser->range += parser->np;

	/* Now consume following elements while they are contiguous */
	while (parser->range + parser->np <= parser->end) {
		u32 flags;
		u64 pci_addr, cpu_addr, size;

		flags = of_bus_pci_get_flags(parser->range);
		pci_addr = of_read_number(parser->range + 1, ns);
		cpu_addr = of_translate_address(parser->node,
				parser->range + na);
		size = of_read_number(parser->range + parser->pna + na, ns);

		if (flags != range->flags)
			break;
		if (pci_addr != range->pci_addr + range->size ||
		    cpu_addr != range->cpu_addr + range->size)
			break;

		range->size += size;
		parser->range += parser->np;
	}

	return range;
}
EXPORT_SYMBOL_GPL(of_pci_range_parser_one);

of_bus_pci_get_flags会去根据特定位,来解析出是IO ,还是MEM

static unsigned int of_bus_pci_get_flags(const __be32 *addr)
{
	unsigned int flags = 0;
	u32 w = be32_to_cpup(addr);

	switch((w >> 24) & 0x03) {
	case 0x01:
		flags |= IORESOURCE_IO;
		break;
	case 0x02: /* 32 bits */
	case 0x03: /* 64 bits */
		flags |= IORESOURCE_MEM;
		break;
	}
	if (w & 0x40000000)
		flags |= IORESOURCE_PREFETCH;
	return flags;
}

of_pci_range_to_resource根据从上面解析出的ranges的flags;来建立不同的struct resource,以供设备驱动使用

int of_pci_range_to_resource(struct of_pci_range *range,
			     struct device_node *np, struct resource *res)
{
	int err;
	res->flags = range->flags;
	res->parent = res->child = res->sibling = NULL;
	res->name = np->full_name;

	if (res->flags & IORESOURCE_IO) {
		unsigned long port;
		err = pci_register_io_range(range->cpu_addr, range->size);
		if (err)
			goto invalid_range;
		port = pci_address_to_pio(range->cpu_addr);
		if (port == (unsigned long)-1) {
			err = -EINVAL;
			goto invalid_range;
		}
		res->start = port;
	} else {
		if ((sizeof(resource_size_t) < 8) &&
		    upper_32_bits(range->cpu_addr)) {
			err = -EINVAL;
			goto invalid_range;
		}

		res->start = range->cpu_addr;
	}
	res->end = res->start + range->size - 1;
	return 0;

invalid_range:
	res->start = (resource_size_t)OF_BAD_ADDR;
	res->end = (resource_size_t)OF_BAD_ADDR;
	return err;
}

static int sbsa_uart_probe(struct platform_device *pdev) { struct uart_amba_port *uap; struct resource *r; int portnr, ret; int baudrate; /* * Check the mandatory baud rate parameter in the DT node early * so that we can easily exit with the error. */ if (pdev->dev.of_node) { struct device_node *np = pdev->dev.of_node; ret = of_property_read_u32(np, "current-speed", &baudrate); if (ret) return ret; } else { baudrate = 115200; } portnr = pl011_find_free_port(); if (portnr < 0) return portnr; uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), GFP_KERNEL); if (!uap) return -ENOMEM; ret = platform_get_irq(pdev, 0); if (ret < 0) { if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "cannot obtain irq\n"); return ret; } uap->port.irq = ret; #ifdef CONFIG_ACPI_SPCR_TABLE if (qdf2400_e44_present) { dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n"); uap->vendor = &vendor_qdt_qdf2400_e44; } else #endif uap->vendor = &vendor_sbsa; uap->reg_offset = uap->vendor->reg_offset; uap->fifosize = 32; uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.ops = &sbsa_uart_pops; uap->fixed_baud = baudrate; snprintf(uap->type, sizeof(uap->type), "SBSA"); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ret = pl011_setup_port(&pdev->dev, uap, r, portnr); if (ret) return ret; platform_set_drvdata(pdev, uap); return pl011_register_port(uap); }linux内核uart驱动在设备注册时,使用acpi表定义的波特率来初始化串口,请根据我的要求上述代码,在代码中添加这一功能
06-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值