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.
163 lines
4.1 KiB
163 lines
4.1 KiB
/* |
|
* Copyright (c) 2017 Intel Corporation |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT snps_designware_intc |
|
|
|
/* This implementation supports only the regular irqs |
|
* No support for priority filtering |
|
* No support for vectored interrupts |
|
* Firqs are also not supported |
|
* This implementation works only when sw_isr_table is enabled in zephyr |
|
*/ |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/devicetree/interrupt_controller.h> |
|
#include <zephyr/irq_nextlevel.h> |
|
#include <zephyr/sw_isr_table.h> |
|
#include "intc_dw.h" |
|
#include <soc.h> |
|
#include <zephyr/irq.h> |
|
|
|
static ALWAYS_INLINE void dw_ictl_dispatch_child_isrs(uint32_t intr_status, |
|
uint32_t isr_base_offset) |
|
{ |
|
uint32_t intr_bitpos, intr_offset; |
|
|
|
/* Dispatch lower level ISRs depending upon the bit set */ |
|
while (intr_status) { |
|
intr_bitpos = find_lsb_set(intr_status) - 1; |
|
intr_status &= ~(1 << intr_bitpos); |
|
intr_offset = isr_base_offset + intr_bitpos; |
|
_sw_isr_table[intr_offset].isr( |
|
_sw_isr_table[intr_offset].arg); |
|
} |
|
} |
|
|
|
static int dw_ictl_initialize(const struct device *dev) |
|
{ |
|
const struct dw_ictl_config *config = dev->config; |
|
volatile struct dw_ictl_registers * const regs = |
|
(struct dw_ictl_registers *)config->base_addr; |
|
|
|
/* disable all interrupts */ |
|
regs->irq_inten_l = 0U; |
|
regs->irq_inten_h = 0U; |
|
|
|
return 0; |
|
} |
|
|
|
static void dw_ictl_isr(const struct device *dev) |
|
{ |
|
const struct dw_ictl_config *config = dev->config; |
|
volatile struct dw_ictl_registers * const regs = |
|
(struct dw_ictl_registers *)config->base_addr; |
|
|
|
dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_l, |
|
config->isr_table_offset); |
|
|
|
if (config->numirqs > 32) { |
|
dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_h, |
|
config->isr_table_offset + 32); |
|
} |
|
} |
|
|
|
static inline void dw_ictl_intr_enable(const struct device *dev, |
|
unsigned int irq) |
|
{ |
|
const struct dw_ictl_config *config = dev->config; |
|
volatile struct dw_ictl_registers * const regs = |
|
(struct dw_ictl_registers *)config->base_addr; |
|
|
|
if (irq < 32) { |
|
regs->irq_inten_l |= (1 << irq); |
|
} else { |
|
regs->irq_inten_h |= (1 << (irq - 32)); |
|
} |
|
} |
|
|
|
static inline void dw_ictl_intr_disable(const struct device *dev, |
|
unsigned int irq) |
|
{ |
|
const struct dw_ictl_config *config = dev->config; |
|
volatile struct dw_ictl_registers * const regs = |
|
(struct dw_ictl_registers *)config->base_addr; |
|
|
|
if (irq < 32) { |
|
regs->irq_inten_l &= ~(1 << irq); |
|
} else { |
|
regs->irq_inten_h &= ~(1 << (irq - 32)); |
|
} |
|
} |
|
|
|
static inline unsigned int dw_ictl_intr_get_state(const struct device *dev) |
|
{ |
|
const struct dw_ictl_config *config = dev->config; |
|
volatile struct dw_ictl_registers * const regs = |
|
(struct dw_ictl_registers *)config->base_addr; |
|
|
|
if (regs->irq_inten_l) { |
|
return 1; |
|
} |
|
|
|
if (config->numirqs > 32) { |
|
if (regs->irq_inten_h) { |
|
return 1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
static int dw_ictl_intr_get_line_state(const struct device *dev, |
|
unsigned int irq) |
|
{ |
|
const struct dw_ictl_config *config = dev->config; |
|
volatile struct dw_ictl_registers * const regs = |
|
(struct dw_ictl_registers *)config->base_addr; |
|
|
|
if (config->numirqs > 32) { |
|
if ((regs->irq_inten_h & BIT(irq - 32)) != 0) { |
|
return 1; |
|
} |
|
} else { |
|
if ((regs->irq_inten_l & BIT(irq)) != 0) { |
|
return 1; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void dw_ictl_config_irq(const struct device *dev); |
|
|
|
static const struct dw_ictl_config dw_config = { |
|
.base_addr = DT_INST_REG_ADDR(0), |
|
.numirqs = DT_INST_PROP(0, num_irqs), |
|
.isr_table_offset = CONFIG_DW_ISR_TBL_OFFSET, |
|
.config_func = dw_ictl_config_irq, |
|
}; |
|
|
|
static const struct irq_next_level_api dw_ictl_apis = { |
|
.intr_enable = dw_ictl_intr_enable, |
|
.intr_disable = dw_ictl_intr_disable, |
|
.intr_get_state = dw_ictl_intr_get_state, |
|
.intr_get_line_state = dw_ictl_intr_get_line_state, |
|
}; |
|
|
|
DEVICE_DT_INST_DEFINE(0, dw_ictl_initialize, NULL, |
|
NULL, &dw_config, PRE_KERNEL_1, |
|
CONFIG_DW_ICTL_INIT_PRIORITY, &dw_ictl_apis); |
|
|
|
static void dw_ictl_config_irq(const struct device *port) |
|
{ |
|
IRQ_CONNECT(DT_INST_IRQN(0), |
|
DT_INST_IRQ(0, priority), |
|
dw_ictl_isr, |
|
DEVICE_DT_INST_GET(0), |
|
DT_INST_IRQ(0, sense)); |
|
} |
|
|
|
IRQ_PARENT_ENTRY_DEFINE(intc_dw, DEVICE_DT_INST_GET(0), DT_INST_IRQN(0), |
|
INTC_INST_ISR_TBL_OFFSET(0), DT_INST_INTC_GET_AGGREGATOR_LEVEL(0));
|
|
|