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;
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 return 0;
50 }
51
dw_ictl_isr(const struct device * dev)52 static void dw_ictl_isr(const struct device *dev)
53 {
54 const struct dw_ictl_config *config = dev->config;
55 volatile struct dw_ictl_registers * const regs =
56 (struct dw_ictl_registers *)config->base_addr;
57
58 dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_l,
59 config->isr_table_offset);
60
61 if (config->numirqs > 32) {
62 dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_h,
63 config->isr_table_offset + 32);
64 }
65 }
66
dw_ictl_intr_enable(const struct device * dev,unsigned int irq)67 static inline void dw_ictl_intr_enable(const struct device *dev,
68 unsigned int irq)
69 {
70 const struct dw_ictl_config *config = dev->config;
71 volatile struct dw_ictl_registers * const regs =
72 (struct dw_ictl_registers *)config->base_addr;
73
74 if (irq < 32) {
75 regs->irq_inten_l |= (1 << irq);
76 } else {
77 regs->irq_inten_h |= (1 << (irq - 32));
78 }
79 }
80
dw_ictl_intr_disable(const struct device * dev,unsigned int irq)81 static inline void dw_ictl_intr_disable(const struct device *dev,
82 unsigned int irq)
83 {
84 const struct dw_ictl_config *config = dev->config;
85 volatile struct dw_ictl_registers * const regs =
86 (struct dw_ictl_registers *)config->base_addr;
87
88 if (irq < 32) {
89 regs->irq_inten_l &= ~(1 << irq);
90 } else {
91 regs->irq_inten_h &= ~(1 << (irq - 32));
92 }
93 }
94
dw_ictl_intr_get_state(const struct device * dev)95 static inline unsigned int dw_ictl_intr_get_state(const struct device *dev)
96 {
97 const struct dw_ictl_config *config = dev->config;
98 volatile struct dw_ictl_registers * const regs =
99 (struct dw_ictl_registers *)config->base_addr;
100
101 if (regs->irq_inten_l) {
102 return 1;
103 }
104
105 if (config->numirqs > 32) {
106 if (regs->irq_inten_h) {
107 return 1;
108 }
109 }
110 return 0;
111 }
112
dw_ictl_intr_get_line_state(const struct device * dev,unsigned int irq)113 static int dw_ictl_intr_get_line_state(const struct device *dev,
114 unsigned int irq)
115 {
116 const struct dw_ictl_config *config = dev->config;
117 volatile struct dw_ictl_registers * const regs =
118 (struct dw_ictl_registers *)config->base_addr;
119
120 if (config->numirqs > 32) {
121 if ((regs->irq_inten_h & BIT(irq - 32)) != 0) {
122 return 1;
123 }
124 } else {
125 if ((regs->irq_inten_l & BIT(irq)) != 0) {
126 return 1;
127 }
128 }
129
130 return 0;
131 }
132
133 static void dw_ictl_config_irq(const struct device *dev);
134
135 static const struct dw_ictl_config dw_config = {
136 .base_addr = DT_INST_REG_ADDR(0),
137 .numirqs = DT_INST_PROP(0, num_irqs),
138 .isr_table_offset = CONFIG_DW_ISR_TBL_OFFSET,
139 .config_func = dw_ictl_config_irq,
140 };
141
142 static const struct irq_next_level_api dw_ictl_apis = {
143 .intr_enable = dw_ictl_intr_enable,
144 .intr_disable = dw_ictl_intr_disable,
145 .intr_get_state = dw_ictl_intr_get_state,
146 .intr_get_line_state = dw_ictl_intr_get_line_state,
147 };
148
149 DEVICE_DT_INST_DEFINE(0, dw_ictl_initialize, NULL,
150 NULL, &dw_config, PRE_KERNEL_1,
151 CONFIG_DW_ICTL_INIT_PRIORITY, &dw_ictl_apis);
152
dw_ictl_config_irq(const struct device * port)153 static void dw_ictl_config_irq(const struct device *port)
154 {
155 IRQ_CONNECT(DT_INST_IRQN(0),
156 DT_INST_IRQ(0, priority),
157 dw_ictl_isr,
158 DEVICE_DT_INST_GET(0),
159 DT_INST_IRQ(0, sense));
160 }
161
162 IRQ_PARENT_ENTRY_DEFINE(intc_dw, DEVICE_DT_INST_GET(0), DT_INST_IRQN(0),
163 INTC_INST_ISR_TBL_OFFSET(0), DT_INST_INTC_GET_AGGREGATOR_LEVEL(0));
164