1 /*
2  * Copyright (c) 2015 - 2023 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT shared_irq
8 
9 #include <errno.h>
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/shared_irq.h>
14 #include <zephyr/init.h>
15 #include <zephyr/sys/sys_io.h>
16 #include <zephyr/irq.h>
17 
18 #ifdef CONFIG_IOAPIC
19 #include <zephyr/drivers/interrupt_controller/ioapic.h>
20 #endif
21 
22 typedef void (*shared_irq_config_irq_t)(void);
23 
24 struct shared_irq_config {
25 	uint32_t irq_num;
26 	shared_irq_config_irq_t config;
27 	uint32_t client_count;
28 };
29 
30 struct shared_irq_client {
31 	const struct device *isr_dev;
32 	isr_t isr_func;
33 	uint32_t enabled;
34 };
35 
36 struct shared_irq_runtime {
37 	struct shared_irq_client *const client;
38 };
39 
40 /**
41  *  @brief Register a device ISR
42  *  @param dev Pointer to device structure for SHARED_IRQ driver instance.
43  *  @param isr_func Pointer to the ISR function for the device.
44  *  @param isr_dev Pointer to the device that will service the interrupt.
45  */
isr_register(const struct device * dev,isr_t isr_func,const struct device * isr_dev)46 static int isr_register(const struct device *dev, isr_t isr_func,
47 				 const struct device *isr_dev)
48 {
49 	struct shared_irq_runtime *clients = dev->data;
50 	const struct shared_irq_config *config = dev->config;
51 	uint32_t i;
52 
53 	for (i = 0U; i < config->client_count; i++) {
54 		if (!clients->client[i].isr_dev) {
55 			clients->client[i].isr_dev = isr_dev;
56 			clients->client[i].isr_func = isr_func;
57 			return 0;
58 		}
59 	}
60 	return -EIO;
61 }
62 
63 /**
64  *  @brief Enable ISR for device
65  *  @param dev Pointer to device structure for SHARED_IRQ driver instance.
66  *  @param isr_dev Pointer to the device that will service the interrupt.
67  */
enable(const struct device * dev,const struct device * isr_dev)68 static inline int enable(const struct device *dev,
69 			 const struct device *isr_dev)
70 {
71 	struct shared_irq_runtime *clients = dev->data;
72 	const struct shared_irq_config *config = dev->config;
73 	uint32_t i;
74 
75 	for (i = 0U; i < config->client_count; i++) {
76 		if (clients->client[i].isr_dev == isr_dev) {
77 			clients->client[i].enabled = 1U;
78 			irq_enable(config->irq_num);
79 			return 0;
80 		}
81 	}
82 	return -EIO;
83 }
84 
last_enabled_isr(struct shared_irq_runtime * clients,int count)85 static int last_enabled_isr(struct shared_irq_runtime *clients, int count)
86 {
87 	uint32_t i;
88 
89 	for (i = 0U; i < count; i++) {
90 		if (clients->client[i].enabled) {
91 			return 0;
92 		}
93 	}
94 	return 1;
95 }
96 /**
97  *  @brief Disable ISR for device
98  *  @param dev Pointer to device structure for SHARED_IRQ driver instance.
99  *  @param isr_dev Pointer to the device that will service the interrupt.
100  */
disable(const struct device * dev,const struct device * isr_dev)101 static inline int disable(const struct device *dev,
102 			  const struct device *isr_dev)
103 {
104 	struct shared_irq_runtime *clients = dev->data;
105 	const struct shared_irq_config *config = dev->config;
106 	uint32_t i;
107 
108 	for (i = 0U; i < config->client_count; i++) {
109 		if (clients->client[i].isr_dev == isr_dev) {
110 			clients->client[i].enabled = 0U;
111 			if (last_enabled_isr(clients, config->client_count)) {
112 				irq_disable(config->irq_num);
113 			}
114 			return 0;
115 		}
116 	}
117 	return -EIO;
118 }
119 
shared_irq_isr(const struct device * dev)120 static void shared_irq_isr(const struct device *dev)
121 {
122 	struct shared_irq_runtime *clients = dev->data;
123 	const struct shared_irq_config *config = dev->config;
124 	uint32_t i;
125 
126 	for (i = 0U; i < config->client_count; i++) {
127 		if (clients->client[i].isr_dev) {
128 			clients->client[i].isr_func(clients->client[i].isr_dev, config->irq_num);
129 		}
130 	}
131 }
132 
133 static DEVICE_API(shared_irq, api_funcs) = {
134 	.isr_register = isr_register,
135 	.enable = enable,
136 	.disable = disable,
137 };
138 
139 
shared_irq_initialize(const struct device * dev)140 static int shared_irq_initialize(const struct device *dev)
141 {
142 	const struct shared_irq_config *config = dev->config;
143 
144 	config->config();
145 	return 0;
146 }
147 
148 /*
149  * INST_SUPPORTS_DEP_ORDS_CNT: Counts the number of "elements" in
150  * DT_SUPPORTS_DEP_ORDS(n). There is a comma after each ordinal(inc. the last)
151  * Hence FOR_EACH adds "+1" once too often which has to be subtracted in the end.
152  */
153 #define F1(x) 1
154 #define INST_SUPPORTS_DEP_ORDS_CNT(n)  \
155 	(FOR_EACH(F1, (+), DT_INST_SUPPORTS_DEP_ORDS(n)) - 1)
156 
157 #define SHARED_IRQ_CONFIG_FUNC(n)					\
158 void shared_irq_config_func_##n(void)					\
159 {									\
160 	IRQ_CONNECT(DT_INST_IRQN(n),					\
161 		    DT_INST_IRQ(n, priority),				\
162 		    shared_irq_isr,					\
163 		    DEVICE_DT_INST_GET(n),				\
164 		    COND_CODE_1(DT_INST_IRQ_HAS_CELL(n, sense),		\
165 				(DT_INST_IRQ(n, sense)),		\
166 				(0)));					\
167 }
168 
169 #define SHARED_IRQ_INIT(n)						\
170 	SHARED_IRQ_CONFIG_FUNC(n)					\
171 	struct shared_irq_client clients_##n[INST_SUPPORTS_DEP_ORDS_CNT(n)]; \
172 	struct shared_irq_runtime shared_irq_data_##n = {		\
173 		.client = clients_##n					\
174 	};								\
175 									\
176 	const struct shared_irq_config shared_irq_config_##n = {	\
177 		.irq_num = DT_INST_IRQN(n),				\
178 		.client_count = INST_SUPPORTS_DEP_ORDS_CNT(n),		\
179 		.config = shared_irq_config_func_##n			\
180 	};								\
181 	DEVICE_DT_INST_DEFINE(n, shared_irq_initialize,			\
182 			      NULL,					\
183 			      &shared_irq_data_##n,			\
184 			      &shared_irq_config_##n, POST_KERNEL,	\
185 			      CONFIG_SHARED_IRQ_INIT_PRIORITY,		\
186 			      &api_funcs);
187 
188 DT_INST_FOREACH_STATUS_OKAY(SHARED_IRQ_INIT)
189