// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 Western Digital Corporation or its affiliates. * Copyright (C) 2022 Ventana Micro Systems Inc.
*/
ret = aplic_irqdomain_translate(fwspec, priv->gsi_base, &hwirq, &type); if (ret) return ret;
for (i = 0; i < nr_irqs; i++) {
irq_domain_set_info(domain, virq + i, hwirq + i, &aplic_direct_chip,
priv, handle_fasteoi_irq, NULL, NULL);
irq_set_affinity(virq + i, &direct->lmask);
}
/* * To handle an APLIC direct interrupts, we just read the CLAIMI register * which will return highest priority pending interrupt and clear the * pending bit of the interrupt. This process is repeated until CLAIMI * register return zero value.
*/ staticvoid aplic_direct_handle_irq(struct irq_desc *desc)
{ struct aplic_idc *idc = this_cpu_ptr(&aplic_idcs); struct irq_domain *irqdomain = idc->direct->irqdomain; struct irq_chip *chip = irq_desc_get_chip(desc);
irq_hw_number_t hw_irq; int irq;
direct = devm_kzalloc(dev, sizeof(*direct), GFP_KERNEL); if (!direct) return -ENOMEM;
priv = &direct->priv;
rc = aplic_setup_priv(priv, dev, regs); if (rc) {
dev_err(dev, "failed to create APLIC context\n"); return rc;
}
/* Setup per-CPU IDC and target CPU mask */
current_cpu = get_cpu(); for (i = 0; i < priv->nr_idcs; i++) {
rc = aplic_direct_parse_parent_hwirq(dev, i, &hwirq, &hartid, priv); if (rc) {
dev_warn(dev, "parent irq for IDC%d not found\n", i); continue;
}
/* * Skip interrupts other than external interrupts for * current privilege level.
*/ if (hwirq != RV_IRQ_EXT) continue;
cpu = riscv_hartid_to_cpuid(hartid); if (cpu < 0) {
dev_warn(dev, "invalid cpuid for IDC%d\n", i); continue;
}
cpumask_set_cpu(cpu, &direct->lmask);
idc = per_cpu_ptr(&aplic_idcs, cpu);
rc = riscv_get_hart_index(dev->fwnode, i, &idc->hart_index); if (rc) {
dev_warn(dev, "hart index not found for IDC%d\n", i); continue;
}
idc->regs = priv->regs + APLIC_IDC_BASE + idc->hart_index * APLIC_IDC_SIZE;
idc->direct = direct;
aplic_idc_set_delivery(idc, true);
/* * Boot cpu might not have APLIC hart_index = 0 so check * and update target registers of all interrupts.
*/ if (cpu == current_cpu && idc->hart_index) {
v = FIELD_PREP(APLIC_TARGET_HART_IDX, idc->hart_index);
v |= FIELD_PREP(APLIC_TARGET_IPRIO, APLIC_DEFAULT_PRIORITY); for (j = 1; j <= priv->nr_irqs; j++)
writel(v, priv->regs + APLIC_TARGET_BASE + (j - 1) * sizeof(u32));
}
setup_count++;
}
put_cpu();
/* Find parent domain and register chained handler */
domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
DOMAIN_BUS_ANY); if (!aplic_direct_parent_irq && domain) {
aplic_direct_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT); if (aplic_direct_parent_irq) {
irq_set_chained_handler(aplic_direct_parent_irq,
aplic_direct_handle_irq);
/* * Setup CPUHP notifier to enable parent * interrupt on all CPUs
*/
cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "irqchip/riscv/aplic:starting",
aplic_direct_starting_cpu,
aplic_direct_dying_cpu);
}
}
/* Fail if we were not able to setup IDC for any CPU */ if (!setup_count) return -ENODEV;
/* Setup global config and interrupt delivery */
aplic_init_hw_global(priv, false);
/* Create irq domain instance for the APLIC */
direct->irqdomain = irq_domain_create_linear(dev->fwnode, priv->nr_irqs + 1,
&aplic_direct_irqdomain_ops, priv); if (!direct->irqdomain) {
dev_err(dev, "failed to create direct irq domain\n"); return -ENOMEM;
}
/* Advertise the interrupt controller */
dev_info(dev, "%d interrupts directly connected to %d CPUs\n",
priv->nr_irqs, priv->nr_idcs);
return 0;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.