static int mvebu_sei_probe()

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