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
cnxk_gpio_irq_stack_free(int cpu)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 *
cnxk_gpio_irq_stack_alloc(int cpu)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
cnxk_gpio_irq_handler(int gpio_num)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
cnxk_gpio_irq_init(struct cnxk_gpiochip * gpiochip)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
cnxk_gpio_irq_fini(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
cnxk_gpio_irq_request(int gpio,int cpu)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
cnxk_gpio_irq_free(int gpio)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 }
217