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/irq_nextlevel.h>
18 #include "intc_dw.h"
19 #include <soc.h>
20 #include <zephyr/irq.h>
21 
dw_ictl_dispatch_child_isrs(uint32_t intr_status,uint32_t isr_base_offset)22 static ALWAYS_INLINE void dw_ictl_dispatch_child_isrs(uint32_t intr_status,
23 						      uint32_t isr_base_offset)
24 {
25 	uint32_t intr_bitpos, intr_offset;
26 
27 	/* Dispatch lower level ISRs depending upon the bit set */
28 	while (intr_status) {
29 		intr_bitpos = find_lsb_set(intr_status) - 1;
30 		intr_status &= ~(1 << intr_bitpos);
31 		intr_offset = isr_base_offset + intr_bitpos;
32 		_sw_isr_table[intr_offset].isr(
33 			_sw_isr_table[intr_offset].arg);
34 	}
35 }
36 
dw_ictl_initialize(const struct device * dev)37 static int dw_ictl_initialize(const struct device *dev)
38 {
39 	const struct dw_ictl_config *config = dev->config;
40 	volatile struct dw_ictl_registers * const regs =
41 			(struct dw_ictl_registers *)config->base_addr;
42 
43 	/* disable all interrupts */
44 	regs->irq_inten_l = 0U;
45 	regs->irq_inten_h = 0U;
46 
47 	return 0;
48 }
49 
dw_ictl_isr(const struct device * dev)50 static void dw_ictl_isr(const struct device *dev)
51 {
52 	const struct dw_ictl_config *config = dev->config;
53 	volatile struct dw_ictl_registers * const regs =
54 			(struct dw_ictl_registers *)config->base_addr;
55 
56 	dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_l,
57 				    config->isr_table_offset);
58 
59 	if (config->numirqs > 32) {
60 		dw_ictl_dispatch_child_isrs(regs->irq_finalstatus_h,
61 					    config->isr_table_offset + 32);
62 	}
63 }
64 
dw_ictl_intr_enable(const struct device * dev,unsigned int irq)65 static inline void dw_ictl_intr_enable(const struct device *dev,
66 				       unsigned int irq)
67 {
68 	const struct dw_ictl_config *config = dev->config;
69 	volatile struct dw_ictl_registers * const regs =
70 		(struct dw_ictl_registers *)config->base_addr;
71 
72 	if (irq < 32) {
73 		regs->irq_inten_l |= (1 << irq);
74 	} else {
75 		regs->irq_inten_h |= (1 << (irq - 32));
76 	}
77 }
78 
dw_ictl_intr_disable(const struct device * dev,unsigned int irq)79 static inline void dw_ictl_intr_disable(const struct device *dev,
80 					unsigned int irq)
81 {
82 	const struct dw_ictl_config *config = dev->config;
83 	volatile struct dw_ictl_registers * const regs =
84 		(struct dw_ictl_registers *)config->base_addr;
85 
86 	if (irq < 32) {
87 		regs->irq_inten_l &= ~(1 << irq);
88 	} else {
89 		regs->irq_inten_h &= ~(1 << (irq - 32));
90 	}
91 }
92 
dw_ictl_intr_get_state(const struct device * dev)93 static inline unsigned int dw_ictl_intr_get_state(const struct device *dev)
94 {
95 	const struct dw_ictl_config *config = dev->config;
96 	volatile struct dw_ictl_registers * const regs =
97 		(struct dw_ictl_registers *)config->base_addr;
98 
99 	if (regs->irq_inten_l) {
100 		return 1;
101 	}
102 
103 	if (config->numirqs > 32) {
104 		if (regs->irq_inten_h) {
105 			return 1;
106 		}
107 	}
108 	return 0;
109 }
110 
dw_ictl_intr_get_line_state(const struct device * dev,unsigned int irq)111 static int dw_ictl_intr_get_line_state(const struct device *dev,
112 				       unsigned int irq)
113 {
114 	const struct dw_ictl_config *config = dev->config;
115 	volatile struct dw_ictl_registers * const regs =
116 		(struct dw_ictl_registers *)config->base_addr;
117 
118 	if (config->numirqs > 32) {
119 		if ((regs->irq_inten_h & BIT(irq - 32)) != 0) {
120 			return 1;
121 		}
122 	} else {
123 		if ((regs->irq_inten_l & BIT(irq)) != 0) {
124 			return 1;
125 		}
126 	}
127 
128 	return 0;
129 }
130 
131 static void dw_ictl_config_irq(const struct device *dev);
132 
133 static const struct dw_ictl_config dw_config = {
134 	.base_addr = DT_INST_REG_ADDR(0),
135 	.numirqs = DT_INST_PROP(0, num_irqs),
136 	.isr_table_offset = CONFIG_DW_ISR_TBL_OFFSET,
137 	.config_func = dw_ictl_config_irq,
138 };
139 
140 static const struct irq_next_level_api dw_ictl_apis = {
141 	.intr_enable = dw_ictl_intr_enable,
142 	.intr_disable = dw_ictl_intr_disable,
143 	.intr_get_state = dw_ictl_intr_get_state,
144 	.intr_get_line_state = dw_ictl_intr_get_line_state,
145 };
146 
147 DEVICE_DT_INST_DEFINE(0, dw_ictl_initialize, NULL,
148 		NULL, &dw_config, PRE_KERNEL_1,
149 		CONFIG_DW_ICTL_INIT_PRIORITY, &dw_ictl_apis);
150 
dw_ictl_config_irq(const struct device * port)151 static void dw_ictl_config_irq(const struct device *port)
152 {
153 	IRQ_CONNECT(DT_INST_IRQN(0),
154 		    DT_INST_IRQ(0, priority),
155 		    dw_ictl_isr,
156 		    DEVICE_DT_INST_GET(0),
157 		    DT_INST_IRQ(0, sense));
158 }
159