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