Skip to content

Add architectural support for shared interrupts #61278

@LaurentiuM1234

Description

@LaurentiuM1234

Problem description

Currently, there seems to be no architectural support for shared interrupts. As such, in the following sequence:

  1. irq_connect_dynamic(33, INTID_33_PRIO, some_handler, some_argument);
  2. irq_connect_dynamic(33, INTID_33_PRIO, some_other_handler, some_other_argument);

some_other_handler would overwrite some_handler. Currently there seem to be 2 ways to solve this:

  1. Make use of the intc_shared_irq.c interrupt controller => doesn't scale very well, seems to require a DT node per shared IRQ. Problematic on platforms which have multiple shared IRQs (e.g: i.MX8QM which has the same INTID for groups of x channels)
  2. Handle this yourself in your driver/application => somewhat difficult and annoying in situations such as the one described above. Needs to be done per module which uses a shared IRQ.

Proposed change

As as a way to solve the problem described above, we could add architectural support for shared interrupts. This could be achieved by simply registering a "dummy" ISR for an INTID and calling all registered ISRs for that INTID.

Detailed RFC

More specifically, if the SHARED_IRQ feature is enabled, we could overwrite z_isr_install() such that instead of registering the passed routine and argument in the ISR table, we could register our own "dummy" ISR and pass it a list of routine/arg pairs that it needs to invoke.

The following is a draft of how this could be achieved:

struct shared_irq_client {
    void (*routine)(const void *arg);
    const void *arg;
};

struct shared_irq_data {
    bool enabled;
    struct shared_irq_client clients[CONFIG_SHARED_IRQ_MAX_NUM_CLIENTS];
    uint32_t client_num;
};

static struct shared_irq_data shared_irq_table[CONFIG_NUM_IRQS];

void shared_irq_isr(const void *data)
{
    struct shared_irq_data *irq_data = data;
    int i;

    for (i = 0; i < irq_data->client_num; i++)
        irq_data->clients[i].routine(irq_data->clients[i].arg);
}

/* note: this definition of z_isr_install would overwrite the definition from sw_isr_common.c */
void z_isr_install(unsigned int irq, void (*routine)(const void *), const void *param)
{
    ... some code goes here ...
    /* note: this is only done once per INTID, upon enabling it for the first time */
    _sw_isr_table[table_idx].arg = irq_data;
    _sw_isr_table[table_idx].isr = shared_irq_isr;

    ... some code goes here ...
    /* irq_data points to the entry in shared_irq_table corresponding to passed IRQ */
    irq_data->clients[irq_data->client_num].routine = routine;
    irq_data->clients[irq_data->client_num].arg = param;
    irq_data->client_num++;
}

Dependencies

This feature would be something one can enable/disable with a configuration. As such, if CONFIG_SHARED_INTERRUPTS=y, z_isr_install would have the above definition. Otherwise, the default definition from sw_isr_common.c will be used. In theory there should be no dependencies.

Would this change be something desired by the community? If so, I can submit a PR after I have something functional.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions