raw/cnxk_gpio: add option to select subset of GPIOs
[dpdk.git] / drivers / raw / cnxk_gpio / cnxk_gpio_irq.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2021 Marvell.
3  */
4
5 #include <fcntl.h>
6 #include <pthread.h>
7 #include <sys/ioctl.h>
8 #include <sys/mman.h>
9 #include <sys/queue.h>
10 #include <unistd.h>
11
12 #include <rte_rawdev_pmd.h>
13
14 #include <roc_api.h>
15
16 #include "cnxk_gpio.h"
17
18 #define OTX_IOC_MAGIC 0xF2
19 #define OTX_IOC_SET_GPIO_HANDLER                                               \
20         _IOW(OTX_IOC_MAGIC, 1, struct otx_gpio_usr_data)
21 #define OTX_IOC_CLR_GPIO_HANDLER                                               \
22         _IO(OTX_IOC_MAGIC, 2)
23
24 struct otx_gpio_usr_data {
25         uint64_t isr_base;
26         uint64_t sp;
27         uint64_t cpu;
28         uint64_t gpio_num;
29 };
30
31 struct cnxk_gpio_irq_stack {
32         LIST_ENTRY(cnxk_gpio_irq_stack) next;
33         void *sp_buffer;
34         int cpu;
35         int inuse;
36 };
37
38 struct cnxk_gpio_irqchip {
39         int fd;
40         /* serialize access to this struct */
41         pthread_mutex_t lock;
42         LIST_HEAD(, cnxk_gpio_irq_stack) stacks;
43
44         struct cnxk_gpiochip *gpiochip;
45 };
46
47 static struct cnxk_gpio_irqchip *irqchip;
48
49 static void
50 cnxk_gpio_irq_stack_free(int cpu)
51 {
52         struct cnxk_gpio_irq_stack *stack;
53
54         LIST_FOREACH(stack, &irqchip->stacks, next) {
55                 if (stack->cpu == cpu)
56                         break;
57         }
58
59         if (!stack)
60                 return;
61
62         if (stack->inuse)
63                 stack->inuse--;
64
65         if (stack->inuse == 0) {
66                 LIST_REMOVE(stack, next);
67                 rte_free(stack->sp_buffer);
68                 rte_free(stack);
69         }
70 }
71
72 static void *
73 cnxk_gpio_irq_stack_alloc(int cpu)
74 {
75 #define ARM_STACK_ALIGNMENT (2 * sizeof(void *))
76 #define IRQ_STACK_SIZE 0x200000
77
78         struct cnxk_gpio_irq_stack *stack;
79
80         LIST_FOREACH(stack, &irqchip->stacks, next) {
81                 if (stack->cpu == cpu)
82                         break;
83         }
84
85         if (stack) {
86                 stack->inuse++;
87                 return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
88         }
89
90         stack = rte_malloc(NULL, sizeof(*stack), 0);
91         if (!stack)
92                 return NULL;
93
94         stack->sp_buffer =
95                 rte_zmalloc(NULL, IRQ_STACK_SIZE * 2, ARM_STACK_ALIGNMENT);
96         if (!stack->sp_buffer) {
97                 rte_free(stack);
98                 return NULL;
99         }
100
101         stack->cpu = cpu;
102         stack->inuse = 1;
103         LIST_INSERT_HEAD(&irqchip->stacks, stack, next);
104
105         return (char *)stack->sp_buffer + IRQ_STACK_SIZE;
106 }
107
108 static void
109 cnxk_gpio_irq_handler(int gpio_num)
110 {
111         struct cnxk_gpiochip *gpiochip = irqchip->gpiochip;
112         struct cnxk_gpio *gpio;
113
114         if (gpio_num >= gpiochip->num_gpios)
115                 goto out;
116
117         gpio = gpiochip->gpios[gpio_num];
118         if (likely(gpio->handler))
119                 gpio->handler(gpio_num, gpio->data);
120
121 out:
122         roc_atf_ret();
123 }
124
125 int
126 cnxk_gpio_irq_init(struct cnxk_gpiochip *gpiochip)
127 {
128         if (irqchip)
129                 return 0;
130
131         irqchip = rte_zmalloc(NULL, sizeof(*irqchip), 0);
132         if (!irqchip)
133                 return -ENOMEM;
134
135         irqchip->fd = open("/dev/otx-gpio-ctr", O_RDWR | O_SYNC);
136         if (irqchip->fd < 0) {
137                 rte_free(irqchip);
138                 return -errno;
139         }
140
141         pthread_mutex_init(&irqchip->lock, NULL);
142         LIST_INIT(&irqchip->stacks);
143         irqchip->gpiochip = gpiochip;
144
145         return 0;
146 }
147
148 void
149 cnxk_gpio_irq_fini(void)
150 {
151         if (!irqchip)
152                 return;
153
154         close(irqchip->fd);
155         rte_free(irqchip);
156         irqchip = NULL;
157 }
158
159 int
160 cnxk_gpio_irq_request(int gpio, int cpu)
161 {
162         struct otx_gpio_usr_data data;
163         void *sp;
164         int ret;
165
166         pthread_mutex_lock(&irqchip->lock);
167
168         sp = cnxk_gpio_irq_stack_alloc(cpu);
169         if (!sp) {
170                 ret = -ENOMEM;
171                 goto out_unlock;
172         }
173
174         data.isr_base = (uint64_t)cnxk_gpio_irq_handler;
175         data.sp = (uint64_t)sp;
176         data.cpu = (uint64_t)cpu;
177         data.gpio_num = (uint64_t)gpio;
178
179         mlockall(MCL_CURRENT | MCL_FUTURE);
180         ret = ioctl(irqchip->fd, OTX_IOC_SET_GPIO_HANDLER, &data);
181         if (ret) {
182                 ret = -errno;
183                 goto out_free_stack;
184         }
185
186         pthread_mutex_unlock(&irqchip->lock);
187
188         return 0;
189
190 out_free_stack:
191         cnxk_gpio_irq_stack_free(cpu);
192 out_unlock:
193         pthread_mutex_unlock(&irqchip->lock);
194
195         return ret;
196 }
197
198 int
199 cnxk_gpio_irq_free(int gpio)
200 {
201         int ret;
202
203         pthread_mutex_lock(&irqchip->lock);
204
205         ret = ioctl(irqchip->fd, OTX_IOC_CLR_GPIO_HANDLER, gpio);
206         if (ret) {
207                 pthread_mutex_unlock(&irqchip->lock);
208                 return -errno;
209         }
210
211         cnxk_gpio_irq_stack_free(irqchip->gpiochip->gpios[gpio]->cpu);
212
213         pthread_mutex_unlock(&irqchip->lock);
214
215         return 0;
216 }