You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
614 lines
19 KiB
614 lines
19 KiB
/* |
|
* 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);
|
|
|