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