// SPDX-License-Identifier: GPL-2.0-only /* * EN751221 Interrupt Controller Driver. * * The EcoNet EN751221 Interrupt Controller is a simple interrupt controller * designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can * be routed to either VPE but not both, so to support per-CPU interrupts, a * secondary IRQ number is allocated to control masking/unmasking on VPE#1. In * this driver, these are called "shadow interrupts". The assignment of shadow * interrupts is defined by the SoC integrator when wiring the interrupt lines, * so they are configurable in the device tree. * * If an interrupt (say 30) needs per-CPU capability, the SoC integrator * allocates another IRQ number (say 29) to be its shadow. The device tree * reflects this by adding the pair <30 29> to the "econet,shadow-interrupts" * property. * * When VPE#1 requests IRQ 30, the driver manipulates the mask bit for IRQ 29, * telling the hardware to mask VPE#1's view of IRQ 30. * * Copyright (C) 2025 Caleb James DeLisle <cjd@cjdns.fr>
*/
/** * @membase: Base address of the interrupt controller registers * @interrupt_shadows: Array of all interrupts, for each value, * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt * - else: This is a per-cpu interrupt whose shadow is the value
*/ staticstruct { void __iomem *membase;
u8 interrupt_shadows[IRQ_COUNT];
} econet_intc __ro_after_init;
static DEFINE_RAW_SPINLOCK(irq_lock);
/* IRQs must be disabled */ staticvoid econet_wreg(u32 reg, u32 val, u32 mask)
{
u32 v;
guard(raw_spinlock)(&irq_lock);
v = ioread32(econet_intc.membase + reg);
v &= ~mask;
v |= val & mask;
iowrite32(v, econet_intc.membase + reg);
}
/* IRQs must be disabled */ staticvoid econet_chmask(u32 hwirq, bool unmask)
{
u32 reg, mask;
u8 shadow;
/* * If the IRQ is a shadow, it should never be manipulated directly. * It should only be masked/unmasked as a result of the "real" per-cpu * irq being manipulated by a thread running on VPE#1. * If it is per-cpu (has a shadow), and we're on VPE#1, the shadow is what we mask. * This is single processor only, so smp_processor_id() never exceeds 1.
*/
shadow = econet_intc.interrupt_shadows[hwirq]; if (WARN_ON_ONCE(shadow == IS_SHADOW)) return; elseif (shadow != NOT_PERCPU && smp_processor_id() == 1)
hwirq = shadow;
/* IRQs must be disabled */ staticvoid econet_intc_mask(struct irq_data *d)
{
econet_chmask(d->hwirq, false);
}
/* IRQs must be disabled */ staticvoid econet_intc_unmask(struct irq_data *d)
{
econet_chmask(d->hwirq, true);
}
staticvoid econet_mask_all(void)
{ /* IRQs are generally disabled during init, but guarding here makes it non-obligatory. */
guard(irqsave)();
econet_wreg(REG_MASK0, 0, ~0);
econet_wreg(REG_MASK1, 0, ~0);
}
if (of_property_read_u32_array(node, field, shadows, num_shadows)) {
pr_err("%pOF: Failed to read %s\n", node, field); return -EINVAL;
}
for (int i = 0; i < num_shadows; i += 2) {
u32 shadow = shadows[i + 1];
u32 target = shadows[i];
if (shadow > IRQ_COUNT) {
pr_err("%pOF: %s[%d] shadow(%d) out of range\n",
node, field, i + 1, shadow); continue;
}
if (target >= IRQ_COUNT) {
pr_err("%pOF: %s[%d] target(%d) out of range\n", node, field, i, target); continue;
}
if (econet_intc.interrupt_shadows[target] != NOT_PERCPU) {
pr_err("%pOF: %s[%d] target(%d) already has a shadow\n",
node, field, i, target); continue;
}
if (econet_intc.interrupt_shadows[shadow] != NOT_PERCPU) {
pr_err("%pOF: %s[%d] shadow(%d) already has a target\n",
node, field, i + 1, shadow); continue;
}
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.