108 lines
2.7 KiB
C
108 lines
2.7 KiB
C
// 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;
|
|
}
|