1 //*****************************************************************************
2 //
3 //  am_hal_gpio.c
4 //! @file
5 //!
6 //! @brief Functions for interfacing with the GPIO module
7 //!
8 //! @addtogroup gpio2 GPIO
9 //! @ingroup apollo2hal
10 //! @{
11 //
12 //*****************************************************************************
13 
14 //*****************************************************************************
15 //
16 // Copyright (c) 2017, Ambiq Micro
17 // All rights reserved.
18 //
19 // Redistribution and use in source and binary forms, with or without
20 // modification, are permitted provided that the following conditions are met:
21 //
22 // 1. Redistributions of source code must retain the above copyright notice,
23 // this list of conditions and the following disclaimer.
24 //
25 // 2. Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // 3. Neither the name of the copyright holder nor the names of its
30 // contributors may be used to endorse or promote products derived from this
31 // software without specific prior written permission.
32 //
33 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
37 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43 // POSSIBILITY OF SUCH DAMAGE.
44 //
45 // This is part of revision 1.2.11 of the AmbiqSuite Development Package.
46 //
47 //*****************************************************************************
48 
49 #include <stdint.h>
50 #include <stdbool.h>
51 #include "am_mcu_apollo.h"
52 
53 //*****************************************************************************
54 //
55 // Array of function pointers for handling GPIO interrupts.
56 //
57 //*****************************************************************************
58 am_hal_gpio_handler_t am_hal_gpio_ppfnHandlers[64];
59 
60 //*****************************************************************************
61 //
62 //! @brief Read the configuration information for the given pin..
63 //!
64 //! @param ui32GPIONum is the GPIO number whose configuration we want to read.
65 //!
66 //! This function reads the PADREG, GPIO CFG, and ALTPAD registers for the
67 //! given GPIO and returns them in the following format:
68 //!
69 //! ( (ALTPAD << 16) | (CFG << 8) | PADREG)
70 //!
71 //! This is the same format used by the \e am_hal_gpio_pin_config()
72 //! function-like macro.
73 //!
74 //! @return Pin configuration information.
75 //
76 //*****************************************************************************
77 uint32_t
am_hal_gpio_pin_config_read(uint32_t ui32PinNumber)78 am_hal_gpio_pin_config_read(uint32_t ui32PinNumber)
79 {
80     uint32_t ui32CfgVal, ui32PadregVal, ui32AltPadVal;
81 
82     am_hal_debug_assert_msg(ui32PinNumber <= 63, "Invalid GPIO number.");
83 
84     ui32CfgVal    = AM_HAL_GPIO_CFG_R(ui32PinNumber);
85     ui32PadregVal = AM_HAL_GPIO_PADREG_R(ui32PinNumber);
86     ui32AltPadVal = AM_HAL_GPIO_ALTPADREG_R(ui32PinNumber);
87 
88     return ( (ui32CfgVal    << CFGVAL_GPIOCFG_S)    |
89              (ui32PadregVal << CFGVAL_PADREG_S)     |
90              (ui32AltPadVal << CFGVAL_ALTPAD_S) );
91 }
92 
93 //*****************************************************************************
94 //
95 //! @brief Get the state of ALL GPIOs from the INPUT READ REGISTER.
96 //!
97 //! This function retrieves the state of ALL GPIOs from the INPUT READ
98 //! REGISTER.
99 //!
100 //! @return the state for the requested GPIO or -1 for error.
101 //
102 //*****************************************************************************
103 uint64_t
am_hal_gpio_input_read(void)104 am_hal_gpio_input_read(void)
105 {
106     //
107     // Combine upper or lower GPIO words into one 64 bit return value.
108     //
109     uint64_t ui64RetVal;
110 
111     ui64RetVal  = ((uint64_t) AM_REGn(GPIO, 0, RDB)) << 32;
112     ui64RetVal |= ((uint64_t) AM_REGn(GPIO, 0, RDA)) << 0;
113 
114     return ui64RetVal;
115 }
116 
117 //*****************************************************************************
118 //
119 //! @brief Get the state of ALL GPIOs from the DATA OUTPUT REGISTER.
120 //!
121 //! This function retrieves the state of ALL GPIOs from the DATA OUTPUT
122 //! REGISTER.
123 //!
124 //! @return the state for the requested GPIO or -1 for error.
125 //
126 //*****************************************************************************
127 uint64_t
am_hal_gpio_out_read(void)128 am_hal_gpio_out_read(void)
129 {
130     //
131     // Combine upper or lower GPIO words into one 64 bit return value.
132     //
133     uint64_t ui64RetVal;
134 
135     ui64RetVal  = ((uint64_t) AM_REGn(GPIO, 0, WTB)) << 32;
136     ui64RetVal |= ((uint64_t) AM_REGn(GPIO, 0, WTA)) << 0;
137 
138     return ui64RetVal;
139 }
140 
141 //*****************************************************************************
142 //
143 //! @brief Gets the state of one GPIO from the DATA ENABLE REGISTER.
144 //!
145 //! @param ui32BitNum - GPIO number.
146 //!
147 //! This function gets the state of one GPIO from the DATA ENABLE REGISTER.
148 //!
149 //! @return the current state for the requested GPIO.
150 //
151 //*****************************************************************************
152 uint32_t
am_hal_gpio_out_enable_bit_get(uint32_t ui32BitNum)153 am_hal_gpio_out_enable_bit_get(uint32_t ui32BitNum)
154 {
155     //
156     // Return 0 or 1.
157     //
158 
159     return (AM_HAL_GPIO_EN(ui32BitNum) & AM_HAL_GPIO_EN_M(ui32BitNum)) ? 1 : 0;
160 }
161 
162 //*****************************************************************************
163 //
164 //! @brief Gets the state of ALL GPIOs from the DATA ENABLE REGISTER.
165 //!
166 //! @param ui32BitNum - GPIO number.
167 //!
168 //! This function gets the state of all GPIOs from the DATA ENABLE REGISTER.
169 //!
170 //! @return the current state for the ALL GPIOs.
171 //
172 //*****************************************************************************
173 uint64_t
am_hal_gpio_out_enable_get(void)174 am_hal_gpio_out_enable_get(void)
175 {
176     //
177     // Combine upper or lower GPIO words into one 64 bit return value.
178     //
179     uint64_t ui64RetVal;
180 
181     ui64RetVal  = ((uint64_t) AM_REGn(GPIO, 0, ENB)) << 32;
182     ui64RetVal |= ((uint64_t) AM_REGn(GPIO, 0, ENA)) << 0;
183 
184     return ui64RetVal;
185 }
186 
187 //*****************************************************************************
188 //
189 //! @brief Enable selected GPIO Interrupts.
190 //!
191 //! @param ui64InterruptMask - GPIOs to enable interrupts on.
192 //!
193 //! Use this function to enable the GPIO interrupts.
194 //!
195 //! @return None
196 //
197 //*****************************************************************************
198 void
am_hal_gpio_int_enable(uint64_t ui64InterruptMask)199 am_hal_gpio_int_enable(uint64_t ui64InterruptMask)
200 {
201     //
202     // Enable the interrupts.
203     //
204     AM_REG(GPIO, INT1EN) |= (ui64InterruptMask >> 32);
205     AM_REG(GPIO, INT0EN) |= (ui64InterruptMask & 0xFFFFFFFF);
206 }
207 
208 //*****************************************************************************
209 //
210 //! @brief Enable selected GPIO Interrupts.
211 //!
212 //! Use this function to enable the GPIO interrupts.
213 //!
214 //! @return logical or of all enabled interrupts. Use AM_HAL_GPIO_BITx to mask
215 //! interrupts of interest.
216 //
217 //*****************************************************************************
218 uint64_t
am_hal_gpio_int_enable_get(void)219 am_hal_gpio_int_enable_get(void)
220 {
221     //
222     // Return enabled interrupts.
223     //
224     uint64_t ui64RetVal;
225 
226     ui64RetVal  = ((uint64_t) AM_REGn(GPIO, 0, INT1EN)) << 32;
227     ui64RetVal |= ((uint64_t) AM_REGn(GPIO, 0, INT0EN)) << 0;
228 
229     return ui64RetVal;
230 }
231 
232 //*****************************************************************************
233 //
234 //! @brief Disable selected GPIO Interrupts.
235 //!
236 //! @param ui64InterruptMask - GPIOs to disable interrupts on.
237 //!
238 //! Use this function to disable the GPIO interrupts.
239 //!
240 //! ui64InterruptMask should be a logical or of AM_HAL_GPIO_BITx defines.
241 //!
242 //! @return None
243 //
244 //*****************************************************************************
245 void
am_hal_gpio_int_disable(uint64_t ui64InterruptMask)246 am_hal_gpio_int_disable(uint64_t ui64InterruptMask)
247 {
248     //
249     // Disable the interrupts.
250     //
251     AM_CRITICAL_BEGIN_ASM
252     AM_REG(GPIO, INT1EN) &= ~(ui64InterruptMask >> 32);
253     AM_REG(GPIO, INT0EN) &= ~(ui64InterruptMask & 0xFFFFFFFF);
254     AM_CRITICAL_END_ASM
255 }
256 
257 //*****************************************************************************
258 //
259 //! @brief Clear selected GPIO Interrupts.
260 //!
261 //! @param ui64InterruptMask - GPIOs to clear interrupts on.
262 //!
263 //! Use this function to clear the GPIO interrupts.
264 //!
265 //! ui64InterruptMask should be a logical or of AM_HAL_GPIO_BITx defines.
266 //!
267 //! @return None
268 //
269 //*****************************************************************************
270 void
am_hal_gpio_int_clear(uint64_t ui64InterruptMask)271 am_hal_gpio_int_clear(uint64_t ui64InterruptMask)
272 {
273     //
274     // Clear the interrupts.
275     //
276     AM_CRITICAL_BEGIN_ASM
277     AM_REG(GPIO, INT1CLR) = (ui64InterruptMask >> 32);
278     AM_REG(GPIO, INT0CLR) = (ui64InterruptMask & 0xFFFFFFFF);
279     AM_CRITICAL_END_ASM
280 }
281 
282 //*****************************************************************************
283 //
284 //! @brief Set selected GPIO Interrupts.
285 //!
286 //! @param ui64InterruptMask - GPIOs to set interrupts on.
287 //!
288 //! Use this function to set the GPIO interrupts.
289 //!
290 //! ui64InterruptMask should be a logical or of AM_HAL_GPIO_BITx defines.
291 //!
292 //! @return None
293 //
294 //*****************************************************************************
295 void
am_hal_gpio_int_set(uint64_t ui64InterruptMask)296 am_hal_gpio_int_set(uint64_t ui64InterruptMask)
297 {
298     //
299     // Set the interrupts.
300     //
301     AM_REG(GPIO, INT1SET) = (ui64InterruptMask >> 32);
302     AM_REG(GPIO, INT0SET) = (ui64InterruptMask & 0xFFFFFFFF);
303 }
304 
305 //*****************************************************************************
306 //
307 //! @brief Set selected GPIO Interrupts.
308 //!
309 //! @param bEnabledOnly - return the status of only the enabled interrupts.
310 //!
311 //! Use this function to set the GPIO interrupts.
312 //!
313 //! @return None
314 //
315 //*****************************************************************************
316 uint64_t
am_hal_gpio_int_status_get(bool bEnabledOnly)317 am_hal_gpio_int_status_get(bool bEnabledOnly)
318 {
319     uint64_t ui64RetVal, ui64Mask;
320 
321     //
322     // Combine upper or lower GPIO words into one 64 bit return value.
323     //
324     ui64Mask   = 0xFFFFFFFFFFFFFFFF;
325 
326     AM_CRITICAL_BEGIN_ASM
327     ui64RetVal  = ((uint64_t) AM_REGn(GPIO, 0, INT1STAT)) << 32;
328     ui64RetVal |= ((uint64_t) AM_REGn(GPIO, 0, INT0STAT)) << 0;
329 
330     if ( bEnabledOnly )
331     {
332         ui64Mask    = ((uint64_t) AM_REGn(GPIO, 0, INT1EN)) << 32;
333         ui64Mask   |= ((uint64_t) AM_REGn(GPIO, 0, INT0EN)) << 0;
334     }
335 
336     ui64RetVal &= ui64Mask;
337     AM_CRITICAL_END_ASM
338 
339     return ui64RetVal;
340 }
341 
342 //*****************************************************************************
343 //
344 //! @brief Convenience function for responding to pin interrupts.
345 //!
346 //! @param ui64Status is the interrupt status as returned by
347 //! am_hal_gpio_int_status_get()
348 //!
349 //! This function may be called from am_hal_gpio_isr() to read the status of
350 //! the GPIO interrupts, determine which pin(s) caused the most recent
351 //! interrupt, and call an interrupt handler function to respond. The interrupt
352 //! handler to be called must be first registered with the
353 //! am_hal_gpio_int_register() function.
354 //!
355 //! In the event that multiple GPIO interrupts are active, the corresponding
356 //! interrupt handlers will be called in numerical order by GPIO number
357 //! starting with the lowest GPIO number.
358 //!
359 //! @return None.
360 //
361 //*****************************************************************************
362 void
am_hal_gpio_int_service(uint64_t ui64Status)363 am_hal_gpio_int_service(uint64_t ui64Status)
364 {
365     uint32_t ui32Status;
366     uint32_t ui32Clz;
367 
368     am_hal_gpio_handler_t pfnHandler;
369 
370     //
371     // Handle any active interrupts in the lower 32 bits
372     //
373     ui32Status = (uint32_t) ui64Status;
374     while ( ui32Status )
375     {
376         //
377         // Pick one of any remaining active interrupt bits
378         //
379 #ifdef __IAR_SYSTEMS_ICC__
380         ui32Clz = __CLZ(ui32Status);
381 #else
382         ui32Clz = __builtin_clz(ui32Status);
383 #endif
384 
385         //
386         // Turn off the bit we picked in the working copy
387         //
388         ui32Status &= ~(0x80000000 >> ui32Clz);
389 
390         //
391         // Check the bit handler table to see if there is an interrupt handler
392         // registered for this particular bit.
393         //
394         pfnHandler = am_hal_gpio_ppfnHandlers[31 - ui32Clz];
395         if ( pfnHandler )
396         {
397             //
398             // If we found an interrupt handler routine, call it now.
399             //
400             pfnHandler();
401         }
402     }
403 
404     //
405     // Handle any active interrupts in the upper 32 bits
406     //
407     ui32Status = (uint32_t) (ui64Status >> 32);
408     while ( ui32Status )
409     {
410         //
411         // Pick one of any remaining active interrupt bits
412         //
413 #ifdef __IAR_SYSTEMS_ICC__
414         ui32Clz = __CLZ(ui32Status);
415 #else
416         ui32Clz = __builtin_clz(ui32Status);
417 #endif
418 
419         //
420         // Turn off the bit we picked in the working copy
421         //
422         ui32Status &= ~(0x80000000 >> ui32Clz);
423 
424         //
425         // Check the bit handler table to see if there is an interrupt handler
426         // registered for this particular bit.
427         //
428         pfnHandler = am_hal_gpio_ppfnHandlers[63 - ui32Clz];
429         if ( pfnHandler )
430         {
431             //
432             // If we found an interrupt handler routine, call it now.
433             //
434             pfnHandler();
435         }
436     }
437 }
438 
439 //*****************************************************************************
440 //
441 //! @brief Register an interrupt handler for an individual GPIO pin.
442 //!
443 //! @param ui32GPIONumber - GPIO number to assign this interrupt handler to.
444 //! @param pfnHandler - Function to call when this GPIO interrupt is received.
445 //!
446 //! This function allows the caller to specify a function that should be called
447 //! any time a GPIO interrupt is received on a particular pin. Registering an
448 //! interrupt handler using this function adds the function pointer to an array
449 //! in SRAM. This interrupt handler will be called by am_hal_gpio_int_service()
450 //! whenever the ui64Status parameter indicates that the corresponding pin is
451 //! asserting it's interrupt.
452 //!
453 //! To remove an interrupt handler that has already been registered, the
454 //! pfnHandler parameter may be set to zero.
455 //!
456 //! @note This function will not have any effect unless the
457 //! am_hal_gpio_int_service() function is being used.
458 //!
459 //! @return None.
460 //
461 //*****************************************************************************
462 void
am_hal_gpio_int_register(uint32_t ui32GPIONumber,am_hal_gpio_handler_t pfnHandler)463 am_hal_gpio_int_register(uint32_t ui32GPIONumber,
464                          am_hal_gpio_handler_t pfnHandler)
465 {
466     //
467     // Check to make sure the GPIO number is valid. (Debug builds only)
468     //
469     am_hal_debug_assert_msg(ui32GPIONumber < 64, "GPIO number out of range.");
470 
471     am_hal_gpio_ppfnHandlers[ui32GPIONumber] = pfnHandler;
472 }
473 
474 //*****************************************************************************
475 //
476 //! @brief Get the state of one GPIO polarity bit.
477 //!
478 //! @param ui32BitNum - GPIO number.
479 //!
480 //! This function gets the state of one GPIO polarity bit.
481 //!
482 //! @note When the bit is a zero the interrupt polarity is rising edge.
483 //!
484 //! @return the current polarity.
485 //
486 //*****************************************************************************
487 bool
am_hal_gpio_int_polarity_bit_get(uint32_t ui32BitNum)488 am_hal_gpio_int_polarity_bit_get(uint32_t ui32BitNum)
489 {
490     //
491     // Check the GPIO_CFGx register's interrupt polarity bit corresponding to
492     // this pin number.
493     //
494     return (AM_REGVAL(AM_HAL_GPIO_CFG(ui32BitNum)) &
495             AM_HAL_GPIO_POL_M(ui32BitNum));
496 }
497 
498 //*****************************************************************************
499 //
500 // End Doxygen group.
501 //! @}
502 //
503 //*****************************************************************************
504