Thread (134 messages) 134 messages, 10 authors, 2021-04-01

Re: [RFT PATCH v3 16/27] irqchip/apple-aic: Add support for the Apple Interrupt Controller

From: Andy Shevchenko <hidden>
Date: 2021-03-05 15:06:06
Also in: linux-arch, linux-arm-kernel, linux-doc, linux-samsung-soc, linux-serial, lkml

On Thu, Mar 4, 2021 at 11:41 PM Hector Martin [off-list ref] wrote:
This is the root interrupt controller used on Apple ARM SoCs such as the
M1. This irqchip driver performs multiple functions:

* Handles both IRQs and FIQs

* Drives the AIC peripheral itself (which handles IRQs)

* Dispatches FIQs to downstream hard-wired clients (currently the ARM
  timer).

* Implements a virtual IPI multiplexer to funnel multiple Linux IPIs
  into a single hardware IPI
...
+ *   - <0 nr flags> - hwirq #nr
+ *   - <1 nr flags> - FIQ #nr
+ *     - nr=0  Physical HV timer
+ *     - nr=1  Virtual HV timer
+ *     - nr=2  Physical guest timer
+ *     - nr=3  Virtual guest timer
+ *
Unneeded blank line.
+ */
...
+#define pr_fmt(fmt) "%s: " fmt, __func__
This is not needed, really, if you have unique / distinguishable
messages in the first place.
Rather people include module names, which may be useful.

...
+#define MASK_REG(x)            (4 * ((x) >> 5))
+#define MASK_BIT(x)            BIT((x) & 0x1f)
GENMASK(4,0)

...
+/*
+ * Max 31 bits in IPI SEND register (top bit is self).
+ * >=32-core chips will need code changes anyway.
+ */
+#define AIC_MAX_CPUS           31
I would put it as (32 - 1) to show that the register is actually 32-bit long.

...
+static atomic_t aic_vipi_flag[AIC_MAX_CPUS];
+static atomic_t aic_vipi_enable[AIC_MAX_CPUS];
Isn't it easier to handle these when they are full width, i.e. 32
items per the array?

...
+static int aic_irq_set_affinity(struct irq_data *d,
+                               const struct cpumask *mask_val, bool force)
+{
+       irq_hw_number_t hwirq = irqd_to_hwirq(d);
+       struct aic_irq_chip *ic = irq_data_get_irq_chip_data(d);
+       int cpu;
+
+       if (hwirq > ic->nr_hw)
= ?
+               return -EINVAL;
+
+       if (force)
+               cpu = cpumask_first(mask_val);
+       else
+               cpu = cpumask_any_and(mask_val, cpu_online_mask);
+
+       aic_ic_write(ic, AIC_TARGET_CPU + hwirq * 4, BIT(cpu));
+       irq_data_update_effective_affinity(d, cpumask_of(cpu));
+
+       return IRQ_SET_MASK_OK;
+}
...
+static void aic_fiq_mask(struct irq_data *d)
+{
+       /* Only the guest timers have real mask bits, unfortunately. */
+       switch (d->hwirq) {
+       case AIC_TMR_GUEST_PHYS:
+               sysreg_clear_set_s(SYS_APL_VM_TMR_FIQ_ENA_EL1, VM_TMR_FIQ_ENABLE_P, 0);
+               break;
+       case AIC_TMR_GUEST_VIRT:
+               sysreg_clear_set_s(SYS_APL_VM_TMR_FIQ_ENA_EL1, VM_TMR_FIQ_ENABLE_V, 0);
+               break;
default case? // some compilers may not be happy
Ditto for all similar places in the series.
+       }
+}
...
+#define TIMER_FIRING(x)                                                        \
+       (((x) & (ARCH_TIMER_CTRL_ENABLE | ARCH_TIMER_CTRL_IT_MASK |            \
+                ARCH_TIMER_CTRL_IT_STAT)) ==                                  \
+        (ARCH_TIMER_CTRL_ENABLE | ARCH_TIMER_CTRL_IT_STAT))
It's a bit hard to read. Perhaps

#define FOO_MASK  (_ENABLE | _STAT)
#define _FIRING ... (FOO_MASK | _MASK == FOO_MASK)

?

...
+       if ((read_sysreg_s(SYS_APL_PMCR0_EL1) & (PMCR0_IMODE | PMCR0_IACT))
+                       == (FIELD_PREP(PMCR0_IMODE, PMCR0_IMODE_FIQ) | PMCR0_IACT)) {
It's better to have == on the previous line.

...
+       for_each_set_bit(i, &firing, AIC_NR_SWIPI) {
+               handle_domain_irq(aic_irqc->ipi_domain, i, regs);
+       }
No {} needed.

...
+static int aic_init_smp(struct aic_irq_chip *irqc, struct device_node *node)
+{
+       int base_ipi;
Introducing a temporary variable may help with readability

...  *d = irqc->hw_domain;
+       irqc->ipi_domain = irq_domain_create_linear(irqc->hw_domain->fwnode, AIC_NR_SWIPI,
+                                                   &aic_ipi_domain_ops, irqc);
+       if (WARN_ON(!irqc->ipi_domain))
+               return -ENODEV;
+
+       irqc->ipi_domain->flags |= IRQ_DOMAIN_FLAG_IPI_SINGLE;
+       irq_domain_update_bus_token(irqc->ipi_domain, DOMAIN_BUS_IPI);
+
+       base_ipi = __irq_domain_alloc_irqs(irqc->ipi_domain, -1, AIC_NR_SWIPI,
+                                          NUMA_NO_NODE, NULL, false, NULL);
+
+       if (WARN_ON(!base_ipi)) {
+               irq_domain_remove(irqc->ipi_domain);
+               return -ENODEV;
+       }
+
+       set_smp_ipi_range(base_ipi, AIC_NR_SWIPI);
+
+       return 0;
+}
...
+       return 0;
+
Extra blank line.

...
+       irqc->hw_domain = irq_domain_create_linear(of_node_to_fwnode(node),
+                                                  irqc->nr_hw + AIC_NR_FIQ,
+                                                  &aic_irq_domain_ops, irqc);
If you are sure it will be always OF-only, why not to use
irq_domain_add_linear()?

...
+       for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
+               aic_ic_write(irqc, AIC_MASK_SET + i * 4, ~0);
+       for (i = 0; i < BITS_TO_U32(irqc->nr_hw); i++)
+               aic_ic_write(irqc, AIC_SW_CLR + i * 4, ~0);
~0 is a beast when it suddenly gets into > int size.

I would recommend using either GENMASK() if it's a bit field, or
type_MAX values if it's a plain number.

-- 
With Best Regards,
Andy Shevchenko
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help