1 /*
2 * Copyright (c) 2022 Winner Microelectronics Co., Ltd. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 /**
17 * @file wm_gpio.c
18 *
19 * @brief GPIO Driver Module
20 *
21 * @author dave
22 *
23 * Copyright (c) 2014 Winner Microelectronics Co., Ltd.
24 */
25 #include "wm_gpio.h"
26 #include "wm_regs.h"
27 #include "wm_irq.h"
28 #include "wm_osal.h"
29 #include "tls_common.h"
30
31 struct gpio_irq_context {
32 tls_gpio_irq_callback callback;
33 void *arg;
34 };
35
36 static struct gpio_irq_context gpio_context[WM_IO_PB_31 - WM_IO_PA_00 + 1] = {{0, 0}};
37
GPIOA_IRQHandler(void)38 ATTRIBUTE_ISR void GPIOA_IRQHandler(void)
39 {
40 u8 i = 0;
41 u8 found = 0;
42 u32 reg = 0;
43 csi_kernel_intrpt_enter();
44
45 reg = tls_reg_read32(HR_GPIO_MIS);
46
47 for (i = 0; i <= WM_IO_PA_15; i++) {
48 if (reg & BIT(i)) {
49 found = 1;
50 break;
51 }
52 }
53
54 if (found) {
55 if (gpio_context[i].callback != NULL)
56 gpio_context[i].callback(gpio_context[i].arg);
57 }
58 csi_kernel_intrpt_exit();
59 }
60
GPIOB_IRQHandler(void)61 ATTRIBUTE_ISR void GPIOB_IRQHandler(void)
62 {
63 u8 i = 0;
64 u8 found = 0;
65 u32 reg = 0;
66 csi_kernel_intrpt_enter();
67 reg = tls_reg_read32(HR_GPIO_MIS + TLS_IO_AB_OFFSET);
68
69 for (i = WM_IO_PB_00; i <= WM_IO_PB_31; i++) {
70 if (reg & BIT(i - WM_IO_PB_00)) {
71 found = 1;
72 break;
73 }
74 }
75
76 if (found) {
77 if (gpio_context[i].callback != NULL)
78 gpio_context[i].callback(gpio_context[i].arg);
79 }
80 csi_kernel_intrpt_exit();
81 }
82
83 /**
84 * @brief This function is used to config gpio function
85 *
86 * @param[in] gpio_pin gpio pin num
87 * @param[in] dir gpio direction
88 * @param[in] attr gpio attribute
89 *
90 * @return None
91 *
92 * @note
93 */
tls_gpio_cfg(enum tls_io_name gpio_pin,enum tls_gpio_dir dir,enum tls_gpio_attr attr)94 void tls_gpio_cfg(enum tls_io_name gpio_pin, enum tls_gpio_dir dir, enum tls_gpio_attr attr)
95 {
96 u8 pin;
97 u16 offset;
98
99 if (gpio_pin >= WM_IO_PB_00) {
100 pin = gpio_pin - WM_IO_PB_00;
101 offset = TLS_IO_AB_OFFSET;
102 } else {
103 pin = gpio_pin;
104 offset = 0;
105 }
106
107 /* enable gpio function */
108 tls_io_cfg_set(gpio_pin, WM_IO_OPT5_GPIO);
109
110 /* gpio direction */
111 if (WM_GPIO_DIR_OUTPUT == dir)
112 tls_reg_write32(HR_GPIO_DIR + offset, tls_reg_read32(HR_GPIO_DIR + offset) | BIT(pin)); /* 1 set output */
113 else if (WM_GPIO_DIR_INPUT == dir)
114 tls_reg_write32(HR_GPIO_DIR + offset, tls_reg_read32(HR_GPIO_DIR + offset) & (~BIT(pin))); /* 0 set input */
115
116 /* gpio attribute */
117 if (WM_GPIO_ATTR_FLOATING == attr) {
118 /* 1 disable pullup */
119 tls_reg_write32(HR_GPIO_PULLUP_EN + offset, tls_reg_read32(HR_GPIO_PULLUP_EN + offset) | BIT(pin));
120 /* 1 disable pulldown */
121 tls_reg_write32(HR_GPIO_PULLDOWN_EN + offset, tls_reg_read32(HR_GPIO_PULLDOWN_EN + offset)&(~BIT(pin)));
122 }
123 if (WM_GPIO_ATTR_PULLHIGH == attr) {
124 /* 0 enable pullup */
125 tls_reg_write32(HR_GPIO_PULLUP_EN + offset, tls_reg_read32(HR_GPIO_PULLUP_EN + offset) & (~BIT(pin)));
126 /* 0 disable pulldown */
127 tls_reg_write32(HR_GPIO_PULLDOWN_EN + offset, tls_reg_read32(HR_GPIO_PULLDOWN_EN + offset) &(~BIT(pin)));
128 }
129
130 if (WM_GPIO_ATTR_PULLLOW == attr) {
131 /* 0 disable pullup */
132 tls_reg_write32(HR_GPIO_PULLUP_EN + offset, tls_reg_read32(HR_GPIO_PULLUP_EN + offset) | BIT(pin));
133 /* 1 disable pulldown */
134 tls_reg_write32(HR_GPIO_PULLDOWN_EN + offset, tls_reg_read32(HR_GPIO_PULLDOWN_EN + offset) | BIT(pin));
135 }
136 }
137
138 /**
139 * @brief This function is used to read gpio status
140 *
141 * @param[in] gpio_pin gpio pin num
142 *
143 * @retval 0 power level is low
144 * @retval 1 power level is high
145 *
146 * @note None
147 */
tls_gpio_read(enum tls_io_name gpio_pin)148 u8 tls_gpio_read(enum tls_io_name gpio_pin)
149 {
150 u32 reg_en;
151 u32 reg;
152 u8 pin;
153 u16 offset;
154
155 if (gpio_pin >= WM_IO_PB_00) {
156 pin = gpio_pin - WM_IO_PB_00;
157 offset = TLS_IO_AB_OFFSET;
158 } else {
159 pin = gpio_pin;
160 offset = 0;
161 }
162
163 reg_en = tls_reg_read32(HR_GPIO_DATA_EN + offset);
164 tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en | (1 << pin));
165 reg = tls_reg_read32(HR_GPIO_DATA + offset);
166 tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en);
167 if (reg & (0x1 << pin))
168 return 1;
169 else
170 return 0;
171 }
172
173 /**
174 * @brief This function is used to modify gpio status
175 *
176 * @param[in] gpio_pin gpio pin num
177 * @param[in] value power level
178 * 0: low power level
179 * 1: high power level
180 *
181 * @return None
182 *
183 * @note None
184 */
tls_gpio_write(enum tls_io_name gpio_pin,u8 value)185 void tls_gpio_write(enum tls_io_name gpio_pin, u8 value)
186 {
187 u32 cpu_sr = 0;
188 u32 reg;
189 u32 reg_en;
190 u8 pin;
191 u16 offset;
192
193 if (gpio_pin >= WM_IO_PB_00) {
194 pin = gpio_pin - WM_IO_PB_00;
195 offset = TLS_IO_AB_OFFSET;
196 } else {
197 pin = gpio_pin;
198 offset = 0;
199 }
200
201 cpu_sr = tls_os_set_critical();
202
203 reg_en = tls_reg_read32(HR_GPIO_DATA_EN + offset);
204 tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en | (1 << pin));
205
206 reg = tls_reg_read32(HR_GPIO_DATA + offset);
207 if (value)
208 tls_reg_write32(HR_GPIO_DATA + offset, reg | (1 << pin)); /* write high */
209 else
210 tls_reg_write32(HR_GPIO_DATA + offset, reg & (~(1 << pin))); /* write low */
211
212 tls_reg_write32(HR_GPIO_DATA_EN + offset, reg_en);
213
214 tls_os_release_critical(cpu_sr);
215 }
216
217 /**
218 * @brief This function is used to config gpio interrupt
219 *
220 * @param[in] gpio_pin gpio pin num
221 * @param[in] mode interrupt trigger type
222 *
223 * @return None
224 *
225 * @note None
226 */
tls_gpio_irq_enable(enum tls_io_name gpio_pin,enum tls_gpio_irq_trig mode)227 void tls_gpio_irq_enable(enum tls_io_name gpio_pin, enum tls_gpio_irq_trig mode)
228 {
229 u32 reg;
230 u8 pin;
231 u16 offset;
232 u8 vec_no;
233
234 if (gpio_pin >= WM_IO_PB_00) {
235 pin = gpio_pin - WM_IO_PB_00;
236 offset = TLS_IO_AB_OFFSET;
237 vec_no = GPIOB_IRQn;
238 } else {
239 pin = gpio_pin;
240 offset = 0;
241 vec_no = GPIOA_IRQn;
242 }
243
244 switch (mode) {
245 case WM_GPIO_IRQ_TRIG_RISING_EDGE:
246 reg = tls_reg_read32(HR_GPIO_IS + offset);
247 reg &= (~(0x1 << pin));
248 // TLS_DBGPRT_INFO("\r\nrising edge is ret=%x\r\n",reg);
249 tls_reg_write32(HR_GPIO_IS + offset, reg); /* 0 edge trigger */
250 reg = tls_reg_read32(HR_GPIO_IBE + offset);
251 reg &= (~(0x1 << pin));
252 // TLS_DBGPRT_INFO("\r\nrising edge ibe ret=%x\r\n",reg);
253 tls_reg_write32(HR_GPIO_IBE + offset, reg); /* 0 edge trigger */
254 reg = tls_reg_read32(HR_GPIO_IEV + offset);
255 reg |= (0x1 << pin);
256 // TLS_DBGPRT_INFO("\r\nrising edge iev ret=%x\r\n",reg);
257 tls_reg_write32(HR_GPIO_IEV + offset, reg); /* 1 rising edge trigger */
258 break;
259 case WM_GPIO_IRQ_TRIG_FALLING_EDGE:
260 reg = tls_reg_read32(HR_GPIO_IS + offset);
261 reg &= (~(0x1 << pin));
262 // TLS_DBGPRT_INFO("\falling edge is ret=%x\n",reg);
263 tls_reg_write32(HR_GPIO_IS + offset, reg); /* 0 edge trigger */
264 reg = tls_reg_read32(HR_GPIO_IBE + offset);
265 reg &= (~(0x1 << pin));
266 // TLS_DBGPRT_INFO("\falling edge ibe ret=%x\n",reg);
267 tls_reg_write32(HR_GPIO_IBE + offset, reg); /* 0 edge trigger */
268 reg = tls_reg_read32(HR_GPIO_IEV + offset);
269 reg &= (~(0x1 << pin));
270 // TLS_DBGPRT_INFO("\falling edge iev ret=%x\n",reg);
271 tls_reg_write32(HR_GPIO_IEV + offset, reg); /* 0 falling edge trigger */
272 break;
273 case WM_GPIO_IRQ_TRIG_DOUBLE_EDGE:
274 reg = tls_reg_read32(HR_GPIO_IS + offset);
275 tls_reg_write32(HR_GPIO_IS + offset, reg & (~(0x1 << pin))); /* 0 edge trigger */
276 reg = tls_reg_read32(HR_GPIO_IBE + offset);
277 tls_reg_write32(HR_GPIO_IBE + offset, reg | (0x1 << pin)); /* 1 double edge trigger */
278 break;
279 case WM_GPIO_IRQ_TRIG_HIGH_LEVEL:
280 reg = tls_reg_read32(HR_GPIO_IS + offset);
281 tls_reg_write32(HR_GPIO_IS + offset, reg | (0x1 << pin)); /* 1 level trigger */
282 reg = tls_reg_read32(HR_GPIO_IEV + offset);
283 tls_reg_write32(HR_GPIO_IEV + offset, reg | (0x1 << pin)); /* 1 high level trigger */
284 break;
285 case WM_GPIO_IRQ_TRIG_LOW_LEVEL:
286 reg = tls_reg_read32(HR_GPIO_IS + offset);
287 tls_reg_write32(HR_GPIO_IS + offset, reg | (0x1 << pin)); /* 1 level trigger */
288 reg = tls_reg_read32(HR_GPIO_IEV + offset);
289 tls_reg_write32(HR_GPIO_IEV + offset, reg & (~(0x1 << pin))); /* 0 low level trigger */
290 break;
291 }
292
293 reg = tls_reg_read32(HR_GPIO_IE + offset);
294 reg |= (0x1 << pin);
295 tls_reg_write32(HR_GPIO_IE + offset, reg); /* enable interrupt */
296
297 tls_irq_enable(vec_no);
298 }
299
300 /**
301 * @brief This function is used to disable gpio interrupt
302 *
303 * @param[in] gpio_pin gpio pin num
304 *
305 * @return None
306 *
307 * @note None
308 */
tls_gpio_irq_disable(enum tls_io_name gpio_pin)309 void tls_gpio_irq_disable(enum tls_io_name gpio_pin)
310 {
311 u32 reg;
312 u8 pin;
313 u16 offset;
314
315 if (gpio_pin >= WM_IO_PB_00) {
316 pin = gpio_pin - WM_IO_PB_00;
317 offset = TLS_IO_AB_OFFSET;
318 reg = tls_reg_read32(HR_GPIO_IE + offset);
319 tls_reg_write32(HR_GPIO_IE + offset, reg & (~(0x1 << pin))); /* disable interrupt */
320 reg = reg&(~(0x1 << pin));
321 if (reg == 0) {
322 tls_irq_disable(GPIOB_IRQn);
323 }
324 } else {
325 pin = gpio_pin;
326 offset = 0;
327 reg = tls_reg_read32(HR_GPIO_IE + offset);
328 tls_reg_write32(HR_GPIO_IE + offset, reg & (~(0x1 << pin))); /* disable interrupt */
329 reg = (reg&(~(0x1 << pin)))&0xFFFF;
330 if (reg == 0) {
331 tls_irq_disable(GPIOA_IRQn);
332 }
333 }
334 }
335
336 /**
337 * @brief This function is used to get gpio interrupt status
338 *
339 * @param[in] gpio_pin gpio pin num
340 *
341 * @retval 0 no interrupt happened
342 * @retval 1 interrupt happened
343 *
344 * @note None
345 */
tls_get_gpio_irq_status(enum tls_io_name gpio_pin)346 u8 tls_get_gpio_irq_status(enum tls_io_name gpio_pin)
347 {
348 u32 reg;
349 u8 pin;
350 u16 offset;
351
352 if (gpio_pin >= WM_IO_PB_00) {
353 pin = gpio_pin - WM_IO_PB_00;
354 offset = TLS_IO_AB_OFFSET;
355 } else {
356 pin = gpio_pin;
357 offset = 0;
358 }
359
360 reg = tls_reg_read32(HR_GPIO_RIS + offset);
361 if (reg & (0x1 << pin))
362 return 1;
363 else
364 return 0;
365 }
366
367 /**
368 * @brief This function is used to clear gpio interrupt flag
369 *
370 * @param[in] gpio_pin gpio pin num
371 *
372 * @return None
373 *
374 * @note None
375 */
tls_clr_gpio_irq_status(enum tls_io_name gpio_pin)376 void tls_clr_gpio_irq_status(enum tls_io_name gpio_pin)
377 {
378 u8 pin;
379 u16 offset;
380
381 if (gpio_pin >= WM_IO_PB_00) {
382 pin = gpio_pin - WM_IO_PB_00;
383 offset = TLS_IO_AB_OFFSET;
384 } else {
385 pin = gpio_pin;
386 offset = 0;
387 }
388
389 tls_reg_write32(HR_GPIO_IC + offset, (0x1 << pin)); /* 1 clear interrupt status */
390 }
391
392 /**
393 * @brief This function is used to register gpio interrupt
394 *
395 * @param[in] gpio_pin gpio pin num
396 * @param[in] callback the gpio interrupt call back function
397 * @param[in] arg parammeter for the callback
398 *
399 * @return None
400 *
401 * @note
402 * gpio callback function is called in interrupt,
403 * so can not operate the critical data in the callback fuuction,
404 * recommendation to send messages to other tasks to operate it.
405 */
tls_gpio_isr_register(enum tls_io_name gpio_pin,tls_gpio_irq_callback callback,void * arg)406 void tls_gpio_isr_register(enum tls_io_name gpio_pin,
407 tls_gpio_irq_callback callback,
408 void *arg)
409 {
410 gpio_context[gpio_pin].callback = callback;
411 gpio_context[gpio_pin].arg = arg;
412 }