1 /*
2 * Copyright (C) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "hi_osal.h"
20 #include <linux/module.h>
21 #include <linux/kernel.h>
22 #include <linux/printk.h>
23 #include <linux/interrupt.h>
24 #include <linux/version.h>
25 #include <linux/slab.h>
26 #include "securec.h"
27
28 static OSAL_LIST_HEAD(g_irq_list);
29
30 typedef struct irq_info_ {
31 int irq;
32 int __irq;
33 struct osal_list_head node;
34 }irq_info;
35
osal_irq_find_node(int irq,struct osal_list_head * list)36 irq_info *osal_irq_find_node(int irq, struct osal_list_head *list)
37 {
38 irq_info *irq_node = NULL;
39
40 osal_list_for_each_entry(irq_node, list, node) {
41 if (irq_node->irq == irq) {
42 return irq_node;
43 }
44 }
45
46 return NULL;
47 }
48
49 extern int hi_get_irq_byname(char *name);
osal_irq_request(unsigned int irq,osal_irq_handler handler,osal_irq_handler thread_fn,const char * name,void * dev)50 int osal_irq_request(unsigned int irq, osal_irq_handler handler, osal_irq_handler thread_fn, const char *name,
51 void *dev)
52 {
53 int __irq = irq;
54 unsigned long flags = IRQF_SHARED;
55 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
56 irq_info *irq_node = NULL;
57 #endif
58
59 if (name == NULL) {
60 printk("name is NULL ! \n");
61 return 0;
62 }
63
64 if (dev == NULL) {
65 flags = 0;
66 }
67
68 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
69 irq_node = osal_irq_find_node(irq, &g_irq_list);
70 if (irq_node != NULL) {
71 printk("irq %s, irq_num =%dr repeat!!!\n", name, irq);
72 return -1;
73 }
74
75 irq_node = kmalloc(sizeof(irq_info), GFP_KERNEL);
76 if (irq_node == NULL) {
77 printk("kmalloc irq_node:%s failed.\n", name);
78 return -1;
79 }
80
81 __irq = hi_get_irq_byname((char *)name);
82 if (__irq == -1) {
83 printk("hi_get_irq_byname:%s failed.\n", name);
84 kfree(irq_node);
85 return -1;
86 }
87 irq_node->__irq = __irq;
88 irq_node->irq = irq;
89 #endif
90
91 osal_list_add_tail(&irq_node->node, &g_irq_list);
92
93 return request_threaded_irq(__irq, (irq_handler_t)handler, (irq_handler_t)thread_fn, flags, name, dev);
94 }
95 EXPORT_SYMBOL(osal_irq_request);
96
osal_irq_free(unsigned int irq,void * dev)97 void osal_irq_free(unsigned int irq, void *dev)
98 {
99 int __irq = irq;
100
101 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
102 irq_info *irq_node = NULL;
103
104 irq_node = osal_irq_find_node(irq, &g_irq_list);
105 if (irq_node != NULL) {
106 __irq = irq_node->__irq;
107 osal_list_del(&irq_node->node);
108 kfree(irq_node);
109 } else {
110 printk("osal_irq_free irq=%u, is not exist\n", irq);
111 osal_dump_stack();
112 return;
113 }
114 #endif
115
116 free_irq(__irq, dev);
117 }
118 EXPORT_SYMBOL(osal_irq_free);
119
osal_irq_set_affinity(unsigned int irq,const char * name,int cpu_mask)120 int osal_irq_set_affinity(unsigned int irq, const char *name, int cpu_mask)
121 {
122 int __irq = irq;
123 struct cpumask cpumask_set = {0};
124
125 cpumask_clear(&cpumask_set);
126
127 ((OSAL_CPU_0 & (unsigned int)cpu_mask) == 0) ?
128 0 : cpumask_set_cpu(0, &cpumask_set); // cpu0
129
130 ((OSAL_CPU_1 & (unsigned int)cpu_mask) == 0) ?
131 0 : cpumask_set_cpu(1, &cpumask_set); // cpu1
132
133 ((OSAL_CPU_2 & (unsigned int)cpu_mask) == 0) ?
134 0 : cpumask_set_cpu(2, &cpumask_set); // cpu2
135
136 ((OSAL_CPU_3 & (unsigned int)cpu_mask) == 0) ?
137 0 : cpumask_set_cpu(3, &cpumask_set); // cpu 3
138
139 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
140 __irq = hi_get_irq_byname((char *)name);
141 #endif
142
143 return irq_set_affinity_hint(__irq, &cpumask_set);
144 }
145 EXPORT_SYMBOL(osal_irq_set_affinity);
146
osal_in_interrupt(void)147 int osal_in_interrupt(void)
148 {
149 return in_interrupt();
150 }
151 EXPORT_SYMBOL(osal_in_interrupt);
152
153 /*
154 * enable_irq - enable handling of an irq
155 * @irq: Interrupt to enable
156 *
157 * Undoes the effect of one call to disable_irq(). If this
158 * matches the last disable, processing of interrupts on this
159 * IRQ line is re-enabled.
160 * This function may be called from IRQ context only when
161 * desc->irq_data.chip->bus_lock and desc->chip->bus_sync_unlock
162 * are NULL !
163 */
osal_irq_enable(unsigned int irq)164 void osal_irq_enable(unsigned int irq)
165 {
166 int __irq = irq;
167
168 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
169 irq_info *irq_node = NULL;
170
171 irq_node = osal_irq_find_node(irq, &g_irq_list);
172 if (irq_node != NULL) {
173 __irq = irq_node->__irq;
174 } else {
175 printk("osal_irq_enable irq=%u, is not exist\n", irq);
176 return;
177 }
178 #endif
179
180 enable_irq(__irq);
181
182 return;
183 }
184 EXPORT_SYMBOL(osal_irq_enable);
185
186 /*
187 * disable_irq - disable an irq and wait for completion
188 * irq: Interrupt to disable
189 *
190 * Disable the selected interrupt line. Enables and Disables are
191 * nested.
192 * This function waits for any pending IRQ handlers for this interrupt
193 * to complete before returning. If you use this function while
194 * holding a resource the IRQ handler may need you will deadlock.
195 * This function may be called - with care - from IRQ context.
196 **/
osal_irq_disable(unsigned int irq)197 void osal_irq_disable(unsigned int irq)
198 {
199 int __irq = irq;
200
201 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
202 irq_info *irq_node = NULL;
203
204 irq_node = osal_irq_find_node(irq, &g_irq_list);
205 if (irq_node != NULL) {
206 __irq = irq_node->__irq;
207 } else {
208 printk("osal_irq_disable irq=%d, is not exist\n", irq);
209 return;
210 }
211 #endif
212
213 disable_irq(__irq);
214
215 return;
216 }
217 EXPORT_SYMBOL(osal_irq_disable);
218
219 /* tasklet is running only on one CPU simultaneously */
osal_tasklet_init(osal_tasklet * tasklet)220 int osal_tasklet_init(osal_tasklet *tasklet)
221 {
222 int ret = 0;
223 struct tasklet_struct *tasklet_local = NULL;
224 atomic_t i = ATOMIC_INIT(0);
225
226 if (!tasklet) {
227 printk("init tasklet is NULL ! \n");
228 return -1;
229 }
230
231 tasklet_local = (struct tasklet_struct *)kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
232 if (!tasklet_local) {
233 printk("Tasklet initialize when malloc memory failed\n");
234 return -1;
235 }
236
237 ret = memset_s(tasklet_local, sizeof(struct tasklet_struct), 0, sizeof(struct tasklet_struct));
238 if (ret != 0) {
239 return -1;
240 }
241
242 tasklet_local->next = NULL;
243 tasklet_local->state = 0;
244 tasklet_local->count = i;
245 tasklet_local->func = tasklet->handler;
246 tasklet_local->data = tasklet->data;
247
248 tasklet->tasklet = (void*)tasklet_local;
249
250 return 0;
251 }
252 EXPORT_SYMBOL(osal_tasklet_init);
253
254 /*
255 * tasklet update should be called after tasklet init
256 **/
osal_tasklet_update(osal_tasklet * tasklet)257 int osal_tasklet_update(osal_tasklet *tasklet)
258 {
259 struct tasklet_struct *tasklet_local = NULL;
260
261 if (!tasklet) {
262 printk("tasklet setdata input is NULL\n");
263 return -1;
264 }
265
266 tasklet_local = (struct tasklet_struct *) tasklet->tasklet;
267 tasklet_local->data = tasklet->data;
268 tasklet_local->func = tasklet->handler;
269
270 return 0;
271 }
272 EXPORT_SYMBOL(osal_tasklet_update);
273
274 /*
275 * add tasklet to tasklet_hi_vec and start tasklet
276 **/
osal_tasklet_schedule(osal_tasklet * tasklet)277 int osal_tasklet_schedule(osal_tasklet *tasklet)
278 {
279 if (!tasklet) {
280 printk("schedule tasklet is NULL ! \n");
281 return -1;
282 }
283
284 tasklet_schedule((struct tasklet_struct *)tasklet->tasklet);
285
286 return 0;
287 }
288 EXPORT_SYMBOL(osal_tasklet_schedule);
289
290 /* kill tasklet */
osal_tasklet_kill(osal_tasklet * tasklet)291 int osal_tasklet_kill(osal_tasklet *tasklet)
292 {
293 if (!tasklet) {
294 printk("kill tasklet is NULL \n");
295 return -1;
296 }
297
298 tasklet_kill((struct tasklet_struct *)tasklet->tasklet);
299 kfree(tasklet->tasklet);
300 tasklet->tasklet = NULL;
301
302 return 0;
303 }
304 EXPORT_SYMBOL(osal_tasklet_kill);
305