1 /*
2 * Copyright (c) 2017 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT snps_designware_intc
8
9 /* This implementation supports only the regular irqs
10 * No support for priority filtering
11 * No support for vectored interrupts
12 * Firqs are also not supported
13 * This implementation works only when sw_isr_table is enabled in zephyr
14 */
15
16 #include <zephyr/device.h>
17 #include <zephyr/devicetree/interrupt_controller.h>
18 #include <zephyr/irq_nextlevel.h>
19 #include <zephyr/sw_isr_table.h>
20 #include "intc_dw.h"
21 #include <soc.h>
22 #include <zephyr/irq.h>
23
dw_ictl_dispatch_child_isrs(uint32_t intr_status,uint32_t isr_base_offset)24 static ALWAYS_INLINE void dw_ictl_dispatch_child_isrs(uint32_t intr_status,
25 uint32_t isr_base_offset)
26 {
27 uint32_t intr_bitpos, intr_offset;
28
29 /* Dispatch lower level ISRs depending upon the bit set */
30 while (intr_status) {
31 intr_bitpos = find_lsb_set(intr_status) - 1;
32 intr_status &= ~(1 << intr_bitpos);
33 intr_offset = isr_base_offset + intr_bitpos - CONFIG_GEN_IRQ_START_VECTOR;
34 _sw_isr_table[intr_offset].isr(
35 _sw_isr_table[intr_offset].arg);
36 }
37 }
38
dw_ictl_initialize(const struct device * dev)39 static int dw_ictl_initialize(const struct device *dev)
40 {
41 const struct dw_ictl_config *config = dev->config;
42 volatile struct dw_ictl_registers * const regs =
43 (struct dw_ictl_registers *)config->base_addr;
44
45 /* disable all interrupts */
46 regs->irq_inten_l = 0U;
47 regs->irq_inten_h = 0U;
48
49 config->config_func();
50
51 return 0;
52 }
53
dw_ictl_isr(const struct device * dev)54 static void dw_ictl_isr(const struct device *dev)
55 {
56 const struct dw_ictl_config *config = dev->config;
57 volatile struct dw_ictl_registers * const regs =
58 (struct dw_ictl_registers *)config->base_addr;
59
60 dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_l,
61 config->isr_table_offset);
62
63 if (config->numirqs > 32) {
64 dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_h,
65 config->isr_table_offset + 32);
66 }
67 }
68
dw_ictl_intr_enable(const struct device * dev,unsigned int irq)69 static inline void dw_ictl_intr_enable(const struct device *dev,
70 unsigned int irq)
71 {
72 const struct dw_ictl_config *config = dev->config;
73 volatile struct dw_ictl_registers * const regs =
74 (struct dw_ictl_registers *)config->base_addr;
75
76 if (irq < 32) {
77 regs->irq_inten_l |= (1 << irq);
78 } else {
79 regs->irq_inten_h |= (1 << (irq - 32));
80 }
81 }
82
dw_ictl_intr_disable(const struct device * dev,unsigned int irq)83 static inline void dw_ictl_intr_disable(const struct device *dev,
84 unsigned int irq)
85 {
86 const struct dw_ictl_config *config = dev->config;
87 volatile struct dw_ictl_registers * const regs =
88 (struct dw_ictl_registers *)config->base_addr;
89
90 if (irq < 32) {
91 regs->irq_inten_l &= ~(1 << irq);
92 } else {
93 regs->irq_inten_h &= ~(1 << (irq - 32));
94 }
95 }
96
dw_ictl_intr_get_state(const struct device * dev)97 static inline unsigned int dw_ictl_intr_get_state(const struct device *dev)
98 {
99 const struct dw_ictl_config *config = dev->config;
100 volatile struct dw_ictl_registers * const regs =
101 (struct dw_ictl_registers *)config->base_addr;
102
103 if (regs->irq_inten_l) {
104 return 1;
105 }
106
107 if (config->numirqs > 32) {
108 if (regs->irq_inten_h) {
109 return 1;
110 }
111 }
112 return 0;
113 }
114
dw_ictl_intr_get_line_state(const struct device * dev,unsigned int irq)115 static int dw_ictl_intr_get_line_state(const struct device *dev,
116 unsigned int irq)
117 {
118 const struct dw_ictl_config *config = dev->config;
119 volatile struct dw_ictl_registers * const regs =
120 (struct dw_ictl_registers *)config->base_addr;
121
122 if (config->numirqs > 32) {
123 if ((regs->irq_inten_h & BIT(irq - 32)) != 0) {
124 return 1;
125 }
126 } else {
127 if ((regs->irq_inten_l & BIT(irq)) != 0) {
128 return 1;
129 }
130 }
131
132 return 0;
133 }
134
135 static const struct irq_next_level_api dw_ictl_apis = {
136 .intr_enable = dw_ictl_intr_enable,
137 .intr_disable = dw_ictl_intr_disable,
138 .intr_get_state = dw_ictl_intr_get_state,
139 .intr_get_line_state = dw_ictl_intr_get_line_state,
140 };
141
142 #define INTC_DW_DEVICE_INIT(inst) \
143 \
144 static void dw_ictl_config_irq_##inst(void) \
145 { \
146 IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), dw_ictl_isr, \
147 DEVICE_DT_INST_GET(inst), DT_INST_IRQ(inst, sense)); \
148 irq_enable(DT_INST_IRQN(inst)); \
149 } \
150 IRQ_PARENT_ENTRY_DEFINE(intc_dw##inst, DEVICE_DT_INST_GET(inst), DT_INST_IRQN(inst), \
151 INTC_INST_ISR_TBL_OFFSET(inst), \
152 DT_INST_INTC_GET_AGGREGATOR_LEVEL(inst)); \
153 \
154 static const struct dw_ictl_config dw_config_##inst = { \
155 .base_addr = DT_INST_REG_ADDR(inst), \
156 .numirqs = DT_INST_PROP(inst, num_irqs), \
157 .isr_table_offset = INTC_INST_ISR_TBL_OFFSET(inst), \
158 .config_func = dw_ictl_config_irq_##inst, \
159 }; \
160 \
161 DEVICE_DT_INST_DEFINE(inst, dw_ictl_initialize, NULL, NULL, &dw_config_##inst, \
162 PRE_KERNEL_1, CONFIG_DW_ICTL_INIT_PRIORITY, &dw_ictl_apis);
163
164 DT_INST_FOREACH_STATUS_OKAY(INTC_DW_DEVICE_INIT)
165