Re: [PATCH v3 02/14] irqchip: irq-pic32-evic: Add support for PIC32 interrupt controller

From: Thomas Gleixner
Date: Fri Jan 08 2016 - 14:05:02 EST


On Thu, 7 Jan 2016, Joshua Henderson wrote:
> +struct pic_reg {
> + u32 val; /* value register*/

Just a nit. If you want to document your data structure, then please use
KernelDoc. These tail comments are horrible.

> + u32 clr; /* clear register */
> + u32 set; /* set register */
> + u32 inv; /* inv register */
> +} __packed;
> +
> +struct evic {
> + struct pic_reg intcon;
> + struct pic_reg priss;
> + struct pic_reg intstat;
> + struct pic_reg iptmr;
> + struct pic_reg ifs[6];
> + u32 reserved1[8];
> + struct pic_reg iec[6];
> + u32 reserved2[8];
> + struct pic_reg ipc[48];
> + u32 reserved3[64];
> + u32 off[191];

It would be way simpler to parse if you structured it like a table

+ struct pic_reg intcon;
+ struct pic_reg priss;
+ struct pic_reg intstat;
+ struct pic_reg iptmr;
+ struct pic_reg ifs[6];
+ u32 reserved1[8];
+ struct pic_reg iec[6];
+ u32 reserved2[8];
+ struct pic_reg ipc[48];
+ u32 reserved3[64];
+ u32 off[191];

> +} __packed;
> +
> +static int get_ext_irq_index(irq_hw_number_t hw);
> +static void evic_set_ext_irq_polarity(int ext_irq, u32 type);

If you move the functions right here, then you don't need the forward
declarations.

> +/* mask off an interrupt */
> +static inline void mask_pic32_irq(struct irq_data *irqd)
> +{
> + u32 reg, mask;
> + unsigned int hwirq = irqd_to_hwirq(irqd);
> +
> + BIT_REG_MASK(hwirq, reg, mask);
> + writel(mask, &evic_base->iec[reg].clr);
> +}
> +
> +/* unmask an interrupt */
> +static inline void unmask_pic32_irq(struct irq_data *irqd)
> +{
> + u32 reg, mask;
> + unsigned int hwirq = irqd_to_hwirq(irqd);
> +
> + BIT_REG_MASK(hwirq, reg, mask);
> + writel(mask, &evic_base->iec[reg].set);
> +}
> +
> +/* acknowledge an interrupt */
> +static void ack_pic32_irq(struct irq_data *irqd)
> +{
> + u32 reg, mask;
> + unsigned int hwirq = irqd_to_hwirq(irqd);
> +
> + BIT_REG_MASK(hwirq, reg, mask);
> + writel(mask, &evic_base->ifs[reg].clr);

So you invented an open coded variant of the generic irq chip. Just with the
difference that the generic chip caches the mask and the register offsets ....

> +}
> +
> +static int set_type_pic32_irq(struct irq_data *data, unsigned int flow_type)
> +{
> + int index;
> +
> + switch (flow_type) {
> +
> + case IRQ_TYPE_EDGE_RISING:
> + case IRQ_TYPE_EDGE_FALLING:
> + irq_set_handler_locked(data, handle_edge_irq);
> + break;
> +
> + case IRQ_TYPE_LEVEL_HIGH:
> + case IRQ_TYPE_LEVEL_LOW:
> + irq_set_handler_locked(data, handle_fasteoi_irq);
> + break;
> +
> + default:
> + pr_err("Invalid interrupt type !\n");
> + return -EINVAL;
> + }
> +
> + /* set polarity for external interrupts only */
> + index = get_ext_irq_index(data->hwirq);
> + if (index >= 0)
> + evic_set_ext_irq_polarity(index, flow_type);

So for the non external interrupts you set a different handler and be
done. How is that supposed to work? They switch magically from one mode to the
other?

> +static void pic32_bind_evic_interrupt(int irq, int set)
> +{
> + writel(set, &evic_base->off[irq]);
> +}
> +
> +int pic32_get_c0_compare_int(void)
> +{
> + int virq;
> +
> + virq = irq_create_mapping(evic_irq_domain, CORE_TIMER_INTERRUPT);
> + irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
> + return virq;

Why isn't that information retrieved via device tree?

> +}
> +
> +static struct irq_chip pic32_irq_chip = {
> + .name = "PIC32-EVIC",
> + .irq_ack = ack_pic32_irq,
> + .irq_mask = mask_pic32_irq,
> + .irq_unmask = unmask_pic32_irq,
> + .irq_eoi = ack_pic32_irq,
> + .irq_set_type = set_type_pic32_irq,

Again, this want's to be in tabular form, if at all.

> +};
> +
> +static void evic_set_irq_priority(int irq, int priority)
> +{
> + u32 reg, shift;
> +
> + reg = irq / 4;
> + shift = (irq % 4) * 8;
> +
> + /* set priority */
> + writel(INT_MASK << shift, &evic_base->ipc[reg].clr);
> + writel(priority << shift, &evic_base->ipc[reg].set);
> +}
> +
> +static void evic_set_ext_irq_polarity(int ext_irq, u32 type)
> +{
> + if (WARN_ON(ext_irq >= NR_EXT_IRQS))
> + return;

That WARN_ON is really helpful because you already made sure not to call it
for non EXT irqs.

> + switch (type) {
> + case IRQ_TYPE_EDGE_RISING:
> + writel(1 << ext_irq, &evic_base->intcon.set);
> + break;
> + case IRQ_TYPE_EDGE_FALLING:
> + writel(1 << ext_irq, &evic_base->intcon.clr);
> + break;
> + default:
> + pr_err("Invalid external interrupt polarity !\n");
> + }
> +}
> +
> +static int get_ext_irq_index(irq_hw_number_t hw)
> +{
> + switch (hw) {
> + case EXTERNAL_INTERRUPT_0:
> + return 0;
> + case EXTERNAL_INTERRUPT_1:
> + return 1;
> + case EXTERNAL_INTERRUPT_2:
> + return 2;
> + case EXTERNAL_INTERRUPT_3:
> + return 3;
> + case EXTERNAL_INTERRUPT_4:
> + return 4;
> + default:
> + return -1;
> + }
> +}

Why don't you use a seperate irq chip for the ext irqs? You can do that with
the generic chip as well.

irq_alloc_domain_generic_chips(domain, 32, 2, ....)

And then you assign the alternate chip (2) to your ext irqs and have the set
type function only for the alternate chip.

Thanks,

tglx