// SPDX-License-Identifier: GPL-2.0-only /* * Aspeed AST2700 Interrupt Controller. * * Copyright (C) 2026 ASPEED Technology Inc. */ #include "irq-ast2700.h" #define ASPEED_INTC_RANGE_FIXED_CELLS 3U #define ASPEED_INTC_RANGE_OFF_START 0U #define ASPEED_INTC_RANGE_OFF_COUNT 1U #define ASPEED_INTC_RANGE_OFF_PHANDLE 2U /** * aspeed_intc_populate_ranges * @dev: Device owning the interrupt controller node. * @ranges: Destination for parsed range descriptors. * * Return: 0 on success, negative errno on error. */ int aspeed_intc_populate_ranges(struct device *dev, struct aspeed_intc_interrupt_ranges *ranges) { struct aspeed_intc_interrupt_range *arr; const __be32 *pvs, *pve; struct device_node *dn; int len; if (!dev || !ranges) return -EINVAL; dn = dev->of_node; pvs = of_get_property(dn, "aspeed,interrupt-ranges", &len); if (!pvs) return -EINVAL; if (len % sizeof(__be32)) return -EINVAL; /* Over-estimate the range entry count for now */ ranges->ranges = devm_kmalloc_array(dev, len / (ASPEED_INTC_RANGE_FIXED_CELLS * sizeof(__be32)), sizeof(*ranges->ranges), GFP_KERNEL); if (!ranges->ranges) return -ENOMEM; pve = pvs + (len / sizeof(__be32)); for (unsigned int i = 0; pve - pvs >= ASPEED_INTC_RANGE_FIXED_CELLS; i++) { struct aspeed_intc_interrupt_range *r; struct device_node *target; u32 target_cells; target = of_find_node_by_phandle(be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_PHANDLE])); if (!target) return -EINVAL; if (of_property_read_u32(target, "#interrupt-cells", &target_cells)) { of_node_put(target); return -EINVAL; } if (!target_cells || target_cells > IRQ_DOMAIN_IRQ_SPEC_PARAMS) { of_node_put(target); return -EINVAL; } if (pve - pvs < ASPEED_INTC_RANGE_FIXED_CELLS + target_cells) { of_node_put(target); return -EINVAL; } r = &ranges->ranges[i]; r->start = be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_START]); r->count = be32_to_cpu(pvs[ASPEED_INTC_RANGE_OFF_COUNT]); { struct of_phandle_args args = { .np = target, .args_count = target_cells, }; for (u32 j = 0; j < target_cells; j++) args.args[j] = be32_to_cpu(pvs[ASPEED_INTC_RANGE_FIXED_CELLS + j]); of_phandle_args_to_fwspec(target, args.args, args.args_count, &r->upstream); } of_node_put(target); r->domain = irq_find_matching_fwspec(&r->upstream, DOMAIN_BUS_ANY); pvs += ASPEED_INTC_RANGE_FIXED_CELLS + target_cells; ranges->nranges++; } /* Re-fit the range array now we know the entry count */ arr = devm_krealloc_array(dev, ranges->ranges, ranges->nranges, sizeof(*ranges->ranges), GFP_KERNEL); if (!arr) return -ENOMEM; ranges->ranges = arr; return 0; }