in irq-mvebu-sei.c [363:472]
static int mvebu_sei_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct irq_domain *plat_domain;
struct mvebu_sei *sei;
u32 parent_irq;
int ret;
sei = devm_kzalloc(&pdev->dev, sizeof(*sei), GFP_KERNEL);
if (!sei)
return -ENOMEM;
sei->dev = &pdev->dev;
mutex_init(&sei->cp_msi_lock);
raw_spin_lock_init(&sei->mask_lock);
sei->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sei->base = devm_ioremap_resource(sei->dev, sei->res);
if (IS_ERR(sei->base))
return PTR_ERR(sei->base);
/* Retrieve the SEI capabilities with the interrupt ranges */
sei->caps = of_device_get_match_data(&pdev->dev);
if (!sei->caps) {
dev_err(sei->dev,
"Could not retrieve controller capabilities\n");
return -EINVAL;
}
/*
* Reserve the single (top-level) parent SPI IRQ from which all the
* interrupts handled by this driver will be signaled.
*/
parent_irq = irq_of_parse_and_map(node, 0);
if (parent_irq <= 0) {
dev_err(sei->dev, "Failed to retrieve top-level SPI IRQ\n");
return -ENODEV;
}
/* Create the root SEI domain */
sei->sei_domain = irq_domain_create_linear(of_node_to_fwnode(node),
(sei->caps->ap_range.size +
sei->caps->cp_range.size),
&mvebu_sei_domain_ops,
sei);
if (!sei->sei_domain) {
dev_err(sei->dev, "Failed to create SEI IRQ domain\n");
ret = -ENOMEM;
goto dispose_irq;
}
irq_domain_update_bus_token(sei->sei_domain, DOMAIN_BUS_NEXUS);
/* Create the 'wired' domain */
sei->ap_domain = irq_domain_create_hierarchy(sei->sei_domain, 0,
sei->caps->ap_range.size,
of_node_to_fwnode(node),
&mvebu_sei_ap_domain_ops,
sei);
if (!sei->ap_domain) {
dev_err(sei->dev, "Failed to create AP IRQ domain\n");
ret = -ENOMEM;
goto remove_sei_domain;
}
irq_domain_update_bus_token(sei->ap_domain, DOMAIN_BUS_WIRED);
/* Create the 'MSI' domain */
sei->cp_domain = irq_domain_create_hierarchy(sei->sei_domain, 0,
sei->caps->cp_range.size,
of_node_to_fwnode(node),
&mvebu_sei_cp_domain_ops,
sei);
if (!sei->cp_domain) {
pr_err("Failed to create CPs IRQ domain\n");
ret = -ENOMEM;
goto remove_ap_domain;
}
irq_domain_update_bus_token(sei->cp_domain, DOMAIN_BUS_GENERIC_MSI);
plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
&mvebu_sei_msi_domain_info,
sei->cp_domain);
if (!plat_domain) {
pr_err("Failed to create CPs MSI domain\n");
ret = -ENOMEM;
goto remove_cp_domain;
}
mvebu_sei_reset(sei);
irq_set_chained_handler_and_data(parent_irq,
mvebu_sei_handle_cascade_irq,
sei);
return 0;
remove_cp_domain:
irq_domain_remove(sei->cp_domain);
remove_ap_domain:
irq_domain_remove(sei->ap_domain);
remove_sei_domain:
irq_domain_remove(sei->sei_domain);
dispose_irq:
irq_dispose_mapping(parent_irq);
return ret;
}