615 lines
19 KiB
C
615 lines
19 KiB
C
/*
|
|
* Copyright 2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* @brief Driver for NXP's IRQ_STEER IP.
|
|
*
|
|
* Below you may find some useful information that will help you better understand how the
|
|
* driver works. The ">" sign is used to mark ideas that are considered important and should
|
|
* be taken note of.
|
|
*
|
|
* 1) What is the IRQ_STEER IP?
|
|
* - in Zephyr terminology, the IRQ_STEER can be considered an interrupt aggregator. As such,
|
|
* its main goal is to multiplex multiple interrupt lines into a single/multiple ones.
|
|
*
|
|
* 2) How does the IRQ_STEER IP work?
|
|
* - below you may find a diagram meant to give you an intuition regarding the IP's structure
|
|
* and how it works (all of the information below is applicable to i.MX8MP but it can be
|
|
* extended to any NXP SoC using the IRQ_STEER IP):
|
|
*
|
|
* SYSTEM_INTID[0:159]
|
|
* |
|
|
* MASK[0:4]------ |
|
|
* | |
|
|
* +------+
|
|
* | |
|
|
* |32 AND|
|
|
* | |
|
|
* +------+
|
|
* |
|
|
* SET[0:4]------ |
|
|
* | |
|
|
* +------+
|
|
* | |
|
|
* |32 OR |
|
|
* | |
|
|
* +------+
|
|
* |__________ STATUS[0:4]
|
|
* |
|
|
* +------+
|
|
* |GROUP |
|
|
* | BY |
|
|
* | 64 |
|
|
* +------+
|
|
* | | |
|
|
* _____________| | |________________
|
|
* | | |
|
|
* MASTER_IN[0] MASTER_IN[1] MASTER_IN[2]
|
|
* | | |
|
|
* | | |
|
|
* |_____________ | _______________|
|
|
* | | |
|
|
* +------+
|
|
* | |
|
|
* | AND | ---------- MINTDIS[0:2]
|
|
* | |
|
|
* +------+
|
|
* | | |
|
|
* _____________| | |________________
|
|
* | | |
|
|
* MASTER_OUT[0] MASTER_OUT[1] MASTER_OUT[2]
|
|
*
|
|
* - initially, all SYSTEM_INTID are grouped by 32 => 5 groups.
|
|
*
|
|
* > each of these groups is controlled by a MASK, SET and STATUS index as follows:
|
|
*
|
|
* MASK/SET/STATUS[0] => SYSTEM_INTID[159:128]
|
|
* MASK/SET/STATUS[1] => SYSTEM_INTID[127:96]
|
|
* MASK/SET/STATUS[2] => SYSTEM_INTID[95:64]
|
|
* MASK/SET/STATUS[3] => SYSTEM_INTID[63:32]
|
|
* MASK/SET/STATUS[4] => SYSTEM_INTID[31:0]
|
|
*
|
|
* > after that, all SYSTEM_INTID are grouped by 64 as follows:
|
|
*
|
|
* SYSTEM_INTID[159:96] => MASTER_IN[2]
|
|
* SYSTEM_INTID[95:32] => MASTER_IN[1]
|
|
* SYSTEM_INTID[31:0] => MASTER_IN[0]
|
|
*
|
|
* note: MASTER_IN[0] is only responsible for 32 interrupts
|
|
*
|
|
* > the value of MASTER_IN[x] is obtained by OR'ing the input interrupt lines.
|
|
*
|
|
* > the value of MASTER_OUT[x] is obtained by AND'ing MASTER_IN[x] with !MINTDIS[x].
|
|
*
|
|
* - whenever a SYSTEM_INTID is asserted, its corresponding MASTER_OUT signal will also
|
|
* be asserted, thus signaling the target processor.
|
|
*
|
|
* > please note the difference between an IRQ_STEER channel and an IRQ_STEER master output.
|
|
* An IRQ_STEER channel refers to an IRQ_STEER instance (e.g: the DSP uses IRQ_STEER channel
|
|
* 0 a.k.a instance 0). An IRQ_STEER channel has multiple master outputs. For example, in
|
|
* the case of i.MX8MP each IRQ_STEER channel has 3 master outputs since an IRQ_STEER channel
|
|
* routes 160 interrupts (32 for first master output, 64 for second master output, and 64 for
|
|
* the third master output).
|
|
*
|
|
* 3) Using Zephyr's multi-level interrupt support
|
|
* - since Zephyr supports organizing interrupts on multiple levels, we can use this to
|
|
* separate the interrupts in 2 levels:
|
|
* 1) LEVEL 1 INTERRUPTS
|
|
* - these are the interrupts that go directly to the processor (for example,
|
|
* on i.MX8MP the MU can directly assert the DSP's interrupt line 7)
|
|
*
|
|
* 2) LEVEL 2 INTERRUPTS
|
|
* - these interrupts go through IRQ_STEER and are signaled by a single
|
|
* processor interrupt line.
|
|
* - e.g: for i.MX8MP, INTID 34 (SDMA3) goes through IRQ_STEER and is signaled
|
|
* to the DSP by INTID 20 which is a direct interrupt (or LEVEL 1 interrupt).
|
|
*
|
|
* - the following diagram (1) shows the interrupt organization on i.MX8MP:
|
|
* +------------+
|
|
* | |
|
|
* SYSTEM_INTID[31:0] ------ IRQ_STEER_MASTER_0 ---- | 19 |
|
|
* | |
|
|
* SYSTEM_INTID[95:32] ----- IRQ_STEER_MASTER_1 ---- | 20 DSP |
|
|
* | |
|
|
* SYSTEM_INTID[159:96] ---- IRQ_STEER_MASTER_2 ---- | 21 |
|
|
* | |
|
|
* +------------+
|
|
*
|
|
* - as such, asserting a system interrupt will lead to asserting its corresponding DSP
|
|
* interrupt line (for example, if system interrupt 34 is asserted, that would lead to
|
|
* interrupt 20 being asserted)
|
|
*
|
|
* - in the above diagram, SYSTEM_INTID[x] are LEVEL 2 interrupts, while 19, 20, and 21 are
|
|
* LEVEL 1 interrupts.
|
|
*
|
|
* - INTID 19 is the parent of SYSTEM_INTID[31:0] and so on.
|
|
*
|
|
* > before going into how the INTIDs are encoded, we need to distinguish between 3 types of
|
|
* INTIDs:
|
|
* 1) System INTIDs
|
|
* - these are the values that can be found in NXP's TRMs for different
|
|
* SoCs (usually they have the same IDs as the GIC SPIs)
|
|
* - for example, INTID 34 is a system INTID for SDMA3 (i.MX8MP).
|
|
*
|
|
* 2) Zephyr INTIDs
|
|
* - these are the Zephyr-specific encodings of the system INTIDs.
|
|
* - these are used to encode multi-level interrupts (for more information
|
|
* please see [1])
|
|
* > if you need to register an interrupt dynamically, you need to use this
|
|
* encoding when specifying the interrupt.
|
|
*
|
|
* 3) DTS INTIDs
|
|
* - these are the encodings of the system INTIDs used in the DTS.
|
|
* - all of these INTIDs are relative to IRQ_STEER's MASTER_OUTs.
|
|
*
|
|
* > encoding an INTID:
|
|
* 1) SYSTEM INTID => ZEPHYR INTID
|
|
* - the following steps need to be performed:
|
|
*
|
|
* a) Find out which IRQ_STEER MASTER
|
|
* is in charge of aggregating this interrupt.
|
|
* * for instance, SYSTEM_INTID 34 (SDMA3 on i.MX8MP) is
|
|
* aggregated by MASTER 1 as depicted in diagram (1).
|
|
*
|
|
* b) After finding the MASTER aggregator, you need
|
|
* to find the corresponding parent interrupt.
|
|
* * for example, SYSTEM_INTID 34 (SDMA3 on i.MX8MP) is
|
|
* aggregated by MASTER 1, which has the parent INTID 20
|
|
* as depicted in diagram (1) => PARENT_INTID(34) = 20.
|
|
*
|
|
* c) Find the INTID relative to the MASTER aggregator. This is done
|
|
* by subtracting the number of interrupts each of the previous
|
|
* master aggregators is in charge of. If the master aggregator is
|
|
* MASTER 0 then RELATIVE_INTID=SYSTEM_INTID.
|
|
* * for example, SYSTEM_ID 34 is aggregated by MASTER 1.
|
|
* As such, we need to subtract 32 from 34 (because the
|
|
* previous master - MASTER 0 - is in charge of aggregating
|
|
* 32 interrupts) => RELATIVE_INTID(34) = 2.
|
|
*
|
|
* * generally speaking, RELATIVE_INTID can be computed using
|
|
* the following formula (assuming SYSTEM_INTID belongs to
|
|
* MASTER y):
|
|
* RELATIVE_INTID(x) = x -
|
|
* \sum{i=0}^{y - 1} GET_MASTER_INT_NUM(i)
|
|
* where:
|
|
* 1) GET_MASTER_INT_NUM(x) computes the number of
|
|
* interrupts master x aggregates
|
|
* 2) x is the system interrupt
|
|
*
|
|
* * to make sure your computation is correct use the
|
|
* following restriction:
|
|
* 0 <= RELATIVE_INTID(x) < GET_MASTER_INT_NUM(y)
|
|
*
|
|
* d) To the obtained RELATIVE_INTID you need to add the value of 1,
|
|
* left shift the result by the number of bits used to encode the
|
|
* level 1 interrupts (see [1] for details) and OR the parent ID.
|
|
* * for example, RELATIVE_INTID(34) = 2 (i.MX8MP),
|
|
* PARENT_INTID(34) = 20 => ZEPHYR_INTID = ((2 + 1) << 8) | 20
|
|
*
|
|
* * generally speaking, ZEPHYR_INTID can be computed using
|
|
* the following formula:
|
|
* ZEPHYR_INTID(x) = ((RELATIVE_INTID(x) + 1) <<
|
|
* NUM_LVL1_BITS) | PARENT_INTID(x)
|
|
* where:
|
|
* 1) RELATIVE_INTID(x) computes the relative INTID
|
|
* of system interrupt x (step c).
|
|
*
|
|
* 2) NUM_LVL1_BITS is the number of bits used to
|
|
* encode level 1 interrupts.
|
|
*
|
|
* 3) PARENT_INTID(x) computes the parent INTID of a
|
|
* system interrupt x (step b)
|
|
*
|
|
* - all of these steps are performed by to_zephyr_irq().
|
|
* > for interrupts aggregated by MASTER 0 you may skip step c) as
|
|
* RELATIVE_INTID(x) = x.
|
|
*
|
|
* 2) SYSTEM INTID => DTS INTID
|
|
* - for this you just have to compute RELATIVE_INTID as described above in
|
|
* step c).
|
|
* - for example, if an IP uses INTID 34 you'd write its interrupts property
|
|
* as follows (i.MX8MP):
|
|
* interrupts = <&master1 2>;
|
|
*
|
|
* 4) Notes and comments
|
|
* > PLEASE DON'T MISTAKE THE ZEPHYR MULTI-LEVEL INTERRUPT ORGANIZATION WITH THE XTENSA ONE.
|
|
* THEY ARE DIFFERENT THINGS.
|
|
*
|
|
* [1]: https://docs.zephyrproject.org/latest/kernel/services/interrupts.html#multi-level-interrupt-handling
|
|
*/
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/devicetree/interrupt_controller.h>
|
|
#include <zephyr/irq.h>
|
|
#include <fsl_irqsteer.h>
|
|
#include <zephyr/cache.h>
|
|
#include <zephyr/sw_isr_table.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/pm/device_runtime.h>
|
|
#include <zephyr/pm/device.h>
|
|
|
|
#include "sw_isr_common.h"
|
|
|
|
LOG_MODULE_REGISTER(nxp_irqstr);
|
|
|
|
/* used for driver binding */
|
|
#define DT_DRV_COMPAT nxp_irqsteer_intc
|
|
|
|
/* macros used for DTS parsing */
|
|
#define _IRQSTEER_REGISTER_DISPATCHER(node_id) \
|
|
IRQ_CONNECT(DT_IRQN(node_id), \
|
|
DT_IRQ(node_id, priority), \
|
|
irqsteer_isr_dispatcher, \
|
|
&dispatchers[DT_REG_ADDR(node_id)], \
|
|
0)
|
|
|
|
#define _IRQSTEER_DECLARE_DISPATCHER(node_id) \
|
|
{ \
|
|
.dev = DEVICE_DT_GET(DT_PARENT(node_id)), \
|
|
.master_index = DT_REG_ADDR(node_id), \
|
|
.irq = DT_IRQN(node_id), \
|
|
}
|
|
|
|
#define IRQSTEER_DECLARE_DISPATCHERS(parent_id)\
|
|
DT_FOREACH_CHILD_STATUS_OKAY_SEP(parent_id, _IRQSTEER_DECLARE_DISPATCHER, (,))
|
|
|
|
#define IRQSTEER_REGISTER_DISPATCHERS(parent_id)\
|
|
DT_FOREACH_CHILD_STATUS_OKAY_SEP(parent_id, _IRQSTEER_REGISTER_DISPATCHER, (;))
|
|
|
|
/* utility macros */
|
|
#define UINT_TO_IRQSTEER(x) ((IRQSTEER_Type *)(x))
|
|
#define DISPATCHER_REGMAP(disp) \
|
|
(((const struct irqsteer_config *)disp->dev->config)->regmap_phys)
|
|
|
|
struct irqsteer_config {
|
|
uint32_t regmap_phys;
|
|
uint32_t regmap_size;
|
|
struct irqsteer_dispatcher *dispatchers;
|
|
};
|
|
|
|
struct irqsteer_dispatcher {
|
|
const struct device *dev;
|
|
/* which set of interrupts is the dispatcher in charge of? */
|
|
uint32_t master_index;
|
|
/* which interrupt line is the dispatcher tied to? */
|
|
uint32_t irq;
|
|
/* reference count for all IRQs aggregated by dispatcher */
|
|
uint8_t irq_refcnt[CONFIG_MAX_IRQ_PER_AGGREGATOR];
|
|
/* dispatcher lock */
|
|
struct k_spinlock lock;
|
|
/* reference count for dispatcher */
|
|
uint8_t refcnt;
|
|
};
|
|
|
|
static struct irqsteer_dispatcher dispatchers[] = {
|
|
IRQSTEER_DECLARE_DISPATCHERS(DT_NODELABEL(irqsteer))
|
|
};
|
|
|
|
/* used to convert system INTID to zephyr INTID */
|
|
static int to_zephyr_irq(uint32_t regmap, uint32_t irq,
|
|
struct irqsteer_dispatcher *dispatcher)
|
|
{
|
|
int i, idx;
|
|
|
|
idx = irq;
|
|
|
|
for (i = dispatcher->master_index - 1; i >= 0; i--) {
|
|
idx -= IRQSTEER_GetMasterIrqCount(UINT_TO_IRQSTEER(regmap), i);
|
|
}
|
|
|
|
return irq_to_level_2(idx) | dispatcher->irq;
|
|
}
|
|
|
|
/* used to convert master-relative INTID to system INTID */
|
|
static int to_system_irq(uint32_t regmap, int irq, int master_index)
|
|
{
|
|
int i;
|
|
|
|
for (i = master_index - 1; i >= 0; i--) {
|
|
irq += IRQSTEER_GetMasterIrqCount(UINT_TO_IRQSTEER(regmap), i);
|
|
}
|
|
|
|
return irq;
|
|
}
|
|
|
|
/* used to convert zephyr INTID to system INTID */
|
|
static int from_zephyr_irq(uint32_t regmap, uint32_t irq, uint32_t master_index)
|
|
{
|
|
int i, idx;
|
|
|
|
idx = irq;
|
|
|
|
for (i = 0; i < master_index; i++) {
|
|
idx += IRQSTEER_GetMasterIrqCount(UINT_TO_IRQSTEER(regmap), i);
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
static void _irqstr_disp_enable_disable(struct irqsteer_dispatcher *disp,
|
|
bool enable)
|
|
{
|
|
uint32_t regmap = DISPATCHER_REGMAP(disp);
|
|
|
|
if (enable) {
|
|
xtensa_irq_enable(XTENSA_IRQ_NUMBER(disp->irq));
|
|
|
|
IRQSTEER_EnableMasterInterrupt(UINT_TO_IRQSTEER(regmap), disp->irq);
|
|
} else {
|
|
IRQSTEER_DisableMasterInterrupt(UINT_TO_IRQSTEER(regmap), disp->irq);
|
|
|
|
xtensa_irq_disable(XTENSA_IRQ_NUMBER(disp->irq));
|
|
}
|
|
}
|
|
|
|
static void _irqstr_disp_get_unlocked(struct irqsteer_dispatcher *disp)
|
|
{
|
|
int ret;
|
|
|
|
if (disp->refcnt == UINT8_MAX) {
|
|
LOG_WRN("disp for irq %d reference count reached limit", disp->irq);
|
|
return;
|
|
}
|
|
|
|
if (!disp->refcnt) {
|
|
ret = pm_device_runtime_get(disp->dev);
|
|
if (ret < 0) {
|
|
LOG_ERR("failed to enable PM resources: %d", ret);
|
|
return;
|
|
}
|
|
|
|
_irqstr_disp_enable_disable(disp, true);
|
|
}
|
|
|
|
disp->refcnt++;
|
|
|
|
LOG_DBG("get on disp for irq %d results in refcnt: %d",
|
|
disp->irq, disp->refcnt);
|
|
}
|
|
|
|
static void _irqstr_disp_put_unlocked(struct irqsteer_dispatcher *disp)
|
|
{
|
|
int ret;
|
|
|
|
if (!disp->refcnt) {
|
|
LOG_WRN("disp for irq %d already put", disp->irq);
|
|
return;
|
|
}
|
|
|
|
disp->refcnt--;
|
|
|
|
if (!disp->refcnt) {
|
|
_irqstr_disp_enable_disable(disp, false);
|
|
|
|
ret = pm_device_runtime_put(disp->dev);
|
|
if (ret < 0) {
|
|
LOG_ERR("failed to disable PM resources: %d", ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG_DBG("put on disp for irq %d results in refcnt: %d",
|
|
disp->irq, disp->refcnt);
|
|
}
|
|
|
|
static void _irqstr_enable_disable_irq(struct irqsteer_dispatcher *disp,
|
|
uint32_t system_irq, bool enable)
|
|
{
|
|
uint32_t regmap = DISPATCHER_REGMAP(disp);
|
|
|
|
if (enable) {
|
|
IRQSTEER_EnableInterrupt(UINT_TO_IRQSTEER(regmap), system_irq);
|
|
} else {
|
|
IRQSTEER_DisableInterrupt(UINT_TO_IRQSTEER(regmap), system_irq);
|
|
}
|
|
}
|
|
|
|
static void irqstr_request_irq_unlocked(struct irqsteer_dispatcher *disp,
|
|
uint32_t zephyr_irq)
|
|
{
|
|
uint32_t system_irq = from_zephyr_irq(DISPATCHER_REGMAP(disp),
|
|
zephyr_irq, disp->master_index);
|
|
|
|
#ifndef CONFIG_SHARED_INTERRUPTS
|
|
if (disp->irq_refcnt[zephyr_irq]) {
|
|
LOG_WRN("irq %d already requested", system_irq);
|
|
return;
|
|
}
|
|
#endif /* CONFIG_SHARED_INTERRUPTS */
|
|
|
|
if (disp->irq_refcnt[zephyr_irq] == UINT8_MAX) {
|
|
LOG_WRN("irq %d reference count reached limit", system_irq);
|
|
return;
|
|
}
|
|
|
|
if (!disp->irq_refcnt[zephyr_irq]) {
|
|
_irqstr_disp_get_unlocked(disp);
|
|
_irqstr_enable_disable_irq(disp, system_irq, true);
|
|
}
|
|
|
|
disp->irq_refcnt[zephyr_irq]++;
|
|
|
|
LOG_DBG("requested irq %d has refcount %d",
|
|
system_irq, disp->irq_refcnt[zephyr_irq]);
|
|
}
|
|
|
|
static void irqstr_release_irq_unlocked(struct irqsteer_dispatcher *disp,
|
|
uint32_t zephyr_irq)
|
|
{
|
|
uint32_t system_irq = from_zephyr_irq(DISPATCHER_REGMAP(disp),
|
|
zephyr_irq, disp->master_index);
|
|
|
|
if (!disp->irq_refcnt[zephyr_irq]) {
|
|
LOG_WRN("irq %d already released", system_irq);
|
|
return;
|
|
}
|
|
|
|
disp->irq_refcnt[zephyr_irq]--;
|
|
|
|
if (!disp->irq_refcnt[zephyr_irq]) {
|
|
_irqstr_enable_disable_irq(disp, system_irq, false);
|
|
_irqstr_disp_put_unlocked(disp);
|
|
}
|
|
|
|
LOG_DBG("released irq %d has refcount %d",
|
|
system_irq, disp->irq_refcnt[zephyr_irq]);
|
|
}
|
|
|
|
void z_soc_irq_enable_disable(uint32_t irq, bool enable)
|
|
{
|
|
uint32_t parent_irq;
|
|
int i, level2_irq;
|
|
|
|
if (irq_get_level(irq) == 1) {
|
|
/* LEVEL 1 interrupts are DSP direct */
|
|
if (enable) {
|
|
xtensa_irq_enable(XTENSA_IRQ_NUMBER(irq));
|
|
} else {
|
|
xtensa_irq_disable(XTENSA_IRQ_NUMBER(irq));
|
|
}
|
|
return;
|
|
}
|
|
|
|
parent_irq = irq_parent_level_2(irq);
|
|
level2_irq = irq_from_level_2(irq);
|
|
|
|
/* find dispatcher responsible for this interrupt */
|
|
for (i = 0; i < ARRAY_SIZE(dispatchers); i++) {
|
|
if (dispatchers[i].irq != parent_irq) {
|
|
continue;
|
|
}
|
|
|
|
K_SPINLOCK(&dispatchers[i].lock) {
|
|
if (enable) {
|
|
irqstr_request_irq_unlocked(&dispatchers[i], level2_irq);
|
|
} else {
|
|
irqstr_release_irq_unlocked(&dispatchers[i], level2_irq);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
void z_soc_irq_enable(uint32_t irq)
|
|
{
|
|
z_soc_irq_enable_disable(irq, true);
|
|
}
|
|
|
|
void z_soc_irq_disable(uint32_t irq)
|
|
{
|
|
z_soc_irq_enable_disable(irq, false);
|
|
}
|
|
|
|
int z_soc_irq_is_enabled(unsigned int irq)
|
|
{
|
|
uint32_t parent_irq;
|
|
int i;
|
|
const struct irqsteer_config *cfg;
|
|
bool enabled;
|
|
|
|
if (irq_get_level(irq) == 1) {
|
|
/* LEVEL 1 interrupts are DSP direct */
|
|
return xtensa_irq_is_enabled(XTENSA_IRQ_NUMBER(irq));
|
|
}
|
|
|
|
parent_irq = irq_parent_level_2(irq);
|
|
enabled = false;
|
|
|
|
/* find dispatcher responsible for this interrupt */
|
|
for (i = 0; i < ARRAY_SIZE(dispatchers); i++) {
|
|
if (dispatchers[i].irq != parent_irq) {
|
|
continue;
|
|
}
|
|
|
|
cfg = dispatchers[i].dev->config;
|
|
|
|
K_SPINLOCK(&dispatchers[i].lock) {
|
|
enabled = dispatchers[i].irq_refcnt[irq_from_level_2(irq)];
|
|
}
|
|
|
|
return enabled;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static void irqsteer_isr_dispatcher(const void *data)
|
|
{
|
|
struct irqsteer_dispatcher *dispatcher;
|
|
const struct irqsteer_config *cfg;
|
|
uint32_t table_idx;
|
|
int system_irq, zephyr_irq, i;
|
|
uint64_t status;
|
|
|
|
dispatcher = (struct irqsteer_dispatcher *)data;
|
|
cfg = dispatcher->dev->config;
|
|
|
|
/* fetch master interrupts status */
|
|
status = IRQSTEER_GetMasterInterruptsStatus(UINT_TO_IRQSTEER(cfg->regmap_phys),
|
|
dispatcher->master_index);
|
|
|
|
for (i = 0; status; i++) {
|
|
/* if bit 0 is set then that means relative INTID i is asserted */
|
|
if (status & 1) {
|
|
/* convert master-relative INTID to a system INTID */
|
|
system_irq = to_system_irq(cfg->regmap_phys, i,
|
|
dispatcher->master_index);
|
|
|
|
/* convert system INTID to a Zephyr INTID */
|
|
zephyr_irq = to_zephyr_irq(cfg->regmap_phys, system_irq, dispatcher);
|
|
|
|
/* compute index in the SW ISR table */
|
|
table_idx = z_get_sw_isr_table_idx(zephyr_irq);
|
|
|
|
/* call child's ISR */
|
|
_sw_isr_table[table_idx].isr(_sw_isr_table[table_idx].arg);
|
|
}
|
|
|
|
status >>= 1;
|
|
}
|
|
}
|
|
|
|
__maybe_unused static int irqstr_pm_action(const struct device *dev,
|
|
enum pm_device_action action)
|
|
{
|
|
/* nothing to be done here */
|
|
return 0;
|
|
}
|
|
|
|
static int irqsteer_init(const struct device *dev)
|
|
{
|
|
IRQSTEER_REGISTER_DISPATCHERS(DT_NODELABEL(irqsteer));
|
|
|
|
return pm_device_runtime_enable(dev);
|
|
}
|
|
|
|
|
|
/* TODO: do we need to add support for MMU-based SoCs? */
|
|
static struct irqsteer_config irqsteer_config = {
|
|
.regmap_phys = DT_REG_ADDR(DT_NODELABEL(irqsteer)),
|
|
.regmap_size = DT_REG_SIZE(DT_NODELABEL(irqsteer)),
|
|
.dispatchers = dispatchers,
|
|
};
|
|
|
|
/* assumption: only 1 IRQ_STEER instance */
|
|
PM_DEVICE_DT_INST_DEFINE(0, irqstr_pm_action);
|
|
DEVICE_DT_INST_DEFINE(0,
|
|
&irqsteer_init,
|
|
PM_DEVICE_DT_INST_GET(0),
|
|
NULL, &irqsteer_config,
|
|
PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY,
|
|
NULL);
|
|
|
|
#define NXP_IRQSTEER_MASTER_IRQ_ENTRY_DEF(node_id) \
|
|
IRQ_PARENT_ENTRY_DEFINE(CONCAT(nxp_irqsteer_master_, DT_NODE_CHILD_IDX(node_id)), NULL, \
|
|
DT_IRQN(node_id), INTC_CHILD_ISR_TBL_OFFSET(node_id), \
|
|
DT_INTC_GET_AGGREGATOR_LEVEL(node_id));
|
|
|
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(0, NXP_IRQSTEER_MASTER_IRQ_ENTRY_DEF);
|