[PATCH] irqchip: uniphier-aidet: add UniPhier AIDET irqchip driver

Masahiro Yamada yamada.masahiro at socionext.com
Mon Aug 7 04:59:18 PDT 2017


Hi Marc,

Thanks for your comments.


2017-08-07 19:43 GMT+09:00 Marc Zyngier <marc.zyngier at arm.com>:
> On 03/08/17 12:15, Masahiro Yamada wrote:
>> UniPhier SoCs contain AIDET (ARM Interrupt Detector).  This is intended
>> to provide additional features that are not covered by GIC.  The main
>> purpose is to provide logic inverter to support low level and falling
>> edge trigger type for interrupt lines from on-board devices.
>>
>> Signed-off-by: Masahiro Yamada <yamada.masahiro at socionext.com>
>> ---
>>
>>  .../socionext,uniphier-aidet.txt                   |  32 +++
>>  MAINTAINERS                                        |   1 +
>>  drivers/irqchip/Kconfig                            |   5 +
>>  drivers/irqchip/Makefile                           |   1 +
>>  drivers/irqchip/irq-uniphier-aidet.c               | 252 +++++++++++++++++++++
>>  5 files changed, 291 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/interrupt-controller/socionext,uniphier-aidet.txt
>>  create mode 100644 drivers/irqchip/irq-uniphier-aidet.c
>>
>> diff --git a/Documentation/devicetree/bindings/interrupt-controller/socionext,uniphier-aidet.txt b/Documentation/devicetree/bindings/interrupt-controller/socionext,uniphier-aidet.txt
>> new file mode 100644
>> index 000000000000..af57fbccd234
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/interrupt-controller/socionext,uniphier-aidet.txt
>> @@ -0,0 +1,32 @@
>> +UniPhier AIDET
>> +
>> +UniPhier AIDET (ARM Interrupt Detector) is an add-on block for ARM GIC (Generic
>> +Interrupt Controller).  GIC itself can handle only high level and rising edge
>> +interrupts.  The AIDET provides logic inverter to support low level and falling
>> +edge interrupts.
>> +
>> +Required properties:
>> +- compatible: Should be one of the following:
>> +    "socionext,uniphier-ld4-aidet"  - for LD4 SoC
>> +    "socionext,uniphier-pro4-aidet" - for Pro4 SoC
>> +    "socionext,uniphier-sld8-aidet" - for sLD8 SoC
>> +    "socionext,uniphier-pro5-aidet" - for Pro5 SoC
>> +    "socionext,uniphier-pxs2-aidet" - for PXs2/LD6b SoC
>> +    "socionext,uniphier-ld11-aidet" - for LD11 SoC
>> +    "socionext,uniphier-ld20-aidet" - for LD20 SoC
>> +    "socionext,uniphier-pxs3-aidet" - for PXs3 SoC
>> +- reg: Specifies offset and length of the register set for the device.
>> +- interrupt-controller: Identifies the node as an interrupt controller
>> +- #interrupt-cells : Specifies the number of cells needed to encode an interrupt
>> +  source.  The value should be 2.  The first cell defines the interrupt number.
>> +  The second cell specifies the trigger type as defined in interrupts.txt in
>> +  this directory.
>
> "interrupt number" is a pretty vague concept. You probably want to
> explain how it relates to the GIC (is that the INTID? or the SPI number?
> What about PPIs?).

I do not know the term "INITD".

As far as I understood from the register bit arrangements,
this hardware block seems to want to count IRQ numbers starting from SGI/PPI.

reg_offset 0x00:  for IRQ 0 - 31       (SGI/PPI)
reg_offset 0x04:  for IRQ 32 - 63      (SPI 0-31)
reg_offset 0x08:  for IRQ 64 - 95      (SPI 32-63)
reg_offset 0x0c:  for IRQ 96 - 127     (SPI 64-95)
   ...


The reg_offset 0x00 corresponds to PPI of GIC,
but never supported by this hardware.  So, reg_offset 0x00 is never used.

This hardware only supports SPI, and
the reg_offset 0x04 corresponds to the start of SPI.

Perhaps, DT binding can describe
"The first cell defines the interrupt number (SPI + 32)".



>> +     spin_lock_irqsave(&priv->lock, flags);
>> +     tmp = readl(priv->reg_base + reg);
>> +     tmp &= ~mask;
>> +     tmp |= mask & val;
>> +     writel(tmp, priv->reg_base + reg);
>
> Given that these accesses do not seem to rely on anything making it into
> memory before the access, consider using the _relaxed accessors (no need
> for two DSBs here).

Will fix.



>> +     spin_unlock_irqrestore(&priv->lock, flags);
>> +}
>> +
>> +static void uniphier_aidet_detconf_update(struct uniphier_aidet_priv *priv,
>> +                                       unsigned long index, unsigned int val)
>> +{
>> +     unsigned int reg;
>> +     u32 mask;
>> +
>> +     reg = UNIPHIER_AIDET_DETCONF + index / 32 * 4;
>
> What is the purpose of UNIPHIER_AIDET_DETCONF here, which is always 0?

I saw a little more registers in the hardware spec, but
DETCONF was the only register base I thought useful for the Linux driver.

I can remove this macro if desired.



>> +static int uniphier_aidet_irq_set_type(struct irq_data *data, unsigned int type)
>> +{
>> +     struct uniphier_aidet_priv *priv = data->chip_data;
>> +     unsigned int val = 0;
>> +
>> +     /* enable inverter for active low triggers */
>> +     switch (type) {
>> +     case IRQ_TYPE_EDGE_RISING:
>> +     case IRQ_TYPE_LEVEL_HIGH:
>
> Nit: consider moving the "val" assignment here so that the symmetry
> between the two cases becomes obvious.

Will update.



>> +
>> +             /* parent is GIC */
>> +             parent_fwspec.fwnode = domain->parent->fwnode;
>> +             parent_fwspec.param_count = 3;
>> +             parent_fwspec.param[0] = 0;             /* SPI */
>> +             parent_fwspec.param[1] = hwirq - 32;
>> +             parent_fwspec.param[2] = IRQ_TYPE_LEVEL_HIGH;
>
> So this is SPI only? And your first line starts at 32? So what is in the
> 32bit register?


As mentioned above, reg_offset 0 is never used, but I guess
the developer of this hardware block wanted to match the register bit
and hwirq number.

So, I respect that and hwirq 32 of this hardware corresponds to the
start of SPI.




>> +
>> +             ret = irq_domain_alloc_irqs_parent(domain, virq, 1,
>> +                                                &parent_fwspec);
>> +             if (ret)
>> +                     return ret;
>> +
>> +             virq++;
>> +             hwirq++;
>> +     }
>
> Two things here: is there any case where you actually have nr_irqs not
> being 1? As far as I know, this is only used for Multi-MSI, and nothing
> else. And if you do need it, then you should probably have a slightly
> better error handling (you're leaking interrupts on error here).


I always expect nr_irqs == 1.

I did not now how to handle cases where nr_irqs is greater than 1.
How should I change it?

If nr_irqs is not 1, error out immediately?

       if (nr_irqs != 1)
               return -EINVAL;



>> +     priv->domain = irq_domain_add_hierarchy(parent_domain, 0,
>> +                                             UNIPHIER_AIDET_NR_IRQS,
>> +                                             dev->of_node,
>> +                                             &uniphier_aidet_domain_ops,
>> +                                             priv);
>
> You seem to be able to handle multiple AIDET devices, and yet your DT
> description doesn't specify a base/span for the interrupt lines that are
> covered by this device. Is that something you intend to support?


I expect only one instance of this hardware in the system.

If desired, I can allocate struct irq_chip statically:


static struct irq_chip uniphier_aidet_irq_chip = {
      .name = "UniPhier AIDET",
      .irq_mask = ...,
      .irq_unmask = ...,

     ..
};



Thanks.


-- 
Best Regards
Masahiro Yamada



More information about the linux-arm-kernel mailing list