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