1 /***************************************************************************//**
2  * @file
3  * @brief General Purpose IO (GPIO) peripheral API
4  *   devices.
5  *******************************************************************************
6  * # License
7  * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
8  *******************************************************************************
9  *
10  * SPDX-License-Identifier: Zlib
11  *
12  * The licensor of this software is Silicon Laboratories Inc.
13  *
14  * This software is provided 'as-is', without any express or implied
15  * warranty. In no event will the authors be held liable for any damages
16  * arising from the use of this software.
17  *
18  * Permission is granted to anyone to use this software for any purpose,
19  * including commercial applications, and to alter it and redistribute it
20  * freely, subject to the following restrictions:
21  *
22  * 1. The origin of this software must not be misrepresented; you must not
23  *    claim that you wrote the original software. If you use this software
24  *    in a product, an acknowledgment in the product documentation would be
25  *    appreciated but is not required.
26  * 2. Altered source versions must be plainly marked as such, and must not be
27  *    misrepresented as being the original software.
28  * 3. This notice may not be removed or altered from any source distribution.
29  *
30  ******************************************************************************/
31 
32 #include "em_gpio.h"
33 
34 #if defined(GPIO_COUNT) && (GPIO_COUNT > 0)
35 
36 /***************************************************************************//**
37  * @addtogroup gpio GPIO - General Purpose Input/Output
38  * @brief General Purpose Input/Output (GPIO) API
39  * @details
40  *  This module contains functions to control the GPIO peripheral of Silicon
41  *  Labs 32-bit MCUs and SoCs. The GPIO peripheral is used for pin configuration
42  *  and direct pin manipulation and sensing as well as routing for peripheral
43  *  pin connections.
44  * @{
45  ******************************************************************************/
46 
47 /*******************************************************************************
48  *******************************   DEFINES   ***********************************
49  ******************************************************************************/
50 
51 /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */
52 
53 /** Validation of the pin typically usable in assert statements. */
54 #define GPIO_DRIVEMODE_VALID(mode)    ((mode) <= 3)
55 #define GPIO_STRENGTH_VALID(strength) (!((strength)                          \
56                                          & ~(_GPIO_P_CTRL_DRIVESTRENGTH_MASK \
57                                              | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK)))
58 /** @endcond */
59 
60 /*******************************************************************************
61  **************************   GLOBAL FUNCTIONS   *******************************
62  ******************************************************************************/
63 
64 /***************************************************************************//**
65  * @brief
66  *   Sets the pin location of the debug pins (Serial Wire interface).
67  *
68  * @note
69  *   Changing the pins used for debugging uncontrolled, may result in a lockout.
70  *
71  * @param[in] location
72  *   The debug pin location to use (0-3).
73  ******************************************************************************/
GPIO_DbgLocationSet(unsigned int location)74 void GPIO_DbgLocationSet(unsigned int location)
75 {
76 #if defined (_GPIO_ROUTE_SWLOCATION_MASK)
77   EFM_ASSERT(location < AFCHANLOC_MAX);
78 
79   GPIO->ROUTE = (GPIO->ROUTE & ~_GPIO_ROUTE_SWLOCATION_MASK)
80                 | (location << _GPIO_ROUTE_SWLOCATION_SHIFT);
81 #elif defined (_GPIO_ROUTELOC0_SWVLOC_MASK)
82   EFM_ASSERT(location < AFCHANLOC_MAX);
83 
84   GPIO->ROUTELOC0 = (GPIO->ROUTELOC0 & ~_GPIO_ROUTELOC0_SWVLOC_MASK)
85                     | (location << _GPIO_ROUTELOC0_SWVLOC_SHIFT);
86 #else
87   (void)location;
88 #endif
89 }
90 
91 #if defined (_GPIO_P_CTRL_DRIVEMODE_MASK)
92 /***************************************************************************//**
93  * @brief
94  *   Sets drive mode for a GPIO port.
95  *
96  * @param[in] port
97  *   The GPIO port to access.
98  *
99  * @param[in] mode
100  *   Drive mode to use for the port.
101  ******************************************************************************/
GPIO_DriveModeSet(GPIO_Port_TypeDef port,GPIO_DriveMode_TypeDef mode)102 void GPIO_DriveModeSet(GPIO_Port_TypeDef port, GPIO_DriveMode_TypeDef mode)
103 {
104   EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_DRIVEMODE_VALID(mode));
105 
106   GPIO->P[port].CTRL = (GPIO->P[port].CTRL & ~(_GPIO_P_CTRL_DRIVEMODE_MASK))
107                        | (mode << _GPIO_P_CTRL_DRIVEMODE_SHIFT);
108 }
109 #endif
110 
111 #if defined (_GPIO_P_CTRL_DRIVESTRENGTH_MASK)
112 /***************************************************************************//**
113  * @brief
114  *   Sets the drive strength for a GPIO port.
115  *
116  * @param[in] port
117  *   The GPIO port to access.
118  *
119  * @param[in] strength
120  *   The drive strength to use for the port.
121  ******************************************************************************/
GPIO_DriveStrengthSet(GPIO_Port_TypeDef port,GPIO_DriveStrength_TypeDef strength)122 void GPIO_DriveStrengthSet(GPIO_Port_TypeDef port,
123                            GPIO_DriveStrength_TypeDef strength)
124 {
125   EFM_ASSERT(GPIO_PORT_VALID(port) && GPIO_STRENGTH_VALID(strength));
126   BUS_RegMaskedWrite(&GPIO->P[port].CTRL,
127                      _GPIO_P_CTRL_DRIVESTRENGTH_MASK | _GPIO_P_CTRL_DRIVESTRENGTHALT_MASK,
128                      strength);
129 }
130 #endif
131 
132 /***************************************************************************//**
133  * @brief
134  *   Configure the GPIO external pin interrupt.
135  *
136  * @details
137  *   It is recommended to disable interrupts before configuring the GPIO pin interrupt.
138  *   See @ref GPIO_IntDisable() for more information.
139  *
140  *   The GPIO interrupt handler must be in place before enabling the
141  *   interrupt.
142  *
143  *   Notice that any pending interrupt for the selected interrupt is cleared
144  *   by this function.
145  *
146  * @note
147  *   On series 0 devices, the pin number parameter is not used. The
148  *   pin number used on these devices is hardwired to the interrupt with the
149  *   same number. @n
150  *   On series 1 devices, the pin number can be selected freely within a group.
151  *   Interrupt numbers are divided into 4 groups (intNo / 4) and valid pin
152  *   number within the interrupt groups are:
153  *       0: pins 0-3   (interrupt number 0-3)
154  *       1: pins 4-7   (interrupt number 4-7)
155  *       2: pins 8-11  (interrupt number 8-11)
156  *       3: pins 12-15 (interrupt number 12-15)
157  *
158  * @param[in] port
159  *   The port to associate with the @p pin.
160  *
161  * @param[in] pin
162  *   The pin number on the port.
163  *
164  * @param[in] intNo
165  *   The interrupt number to trigger.
166  *
167  * @param[in] risingEdge
168  *   Set to true if the interrupt will be enabled on the rising edge. Otherwise, false.
169  *
170  * @param[in] fallingEdge
171  *   Set to true if the interrupt will be enabled on the falling edge. Otherwise, false.
172  *
173  * @param[in] enable
174  *   Set to true if the interrupt will be enabled after the configuration is complete.
175  *   False to leave disabled. See @ref GPIO_IntDisable() and @ref GPIO_IntEnable().
176  ******************************************************************************/
GPIO_ExtIntConfig(GPIO_Port_TypeDef port,unsigned int pin,unsigned int intNo,bool risingEdge,bool fallingEdge,bool enable)177 void GPIO_ExtIntConfig(GPIO_Port_TypeDef port,
178                        unsigned int pin,
179                        unsigned int intNo,
180                        bool risingEdge,
181                        bool fallingEdge,
182                        bool enable)
183 {
184 #if defined (_GPIO_EXTIPSELH_MASK)
185   uint32_t tmp = 0;
186 #endif
187 #if !defined(_GPIO_EXTIPINSELL_MASK)
188   (void)pin;
189 #endif
190 
191   EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
192 #if defined(_GPIO_EXTIPINSELL_MASK)
193   EFM_ASSERT(GPIO_INTNO_PIN_VALID(intNo, pin));
194 #endif
195 
196   /* The EXTIPSELL register controls pins 0-7 and EXTIPSELH controls
197    * pins 8-15 of the interrupt configuration. */
198   if (intNo < 8) {
199     BUS_RegMaskedWrite(&GPIO->EXTIPSELL,
200                        _GPIO_EXTIPSELL_EXTIPSEL0_MASK
201                        << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo),
202                        (uint32_t)port << (_GPIO_EXTIPSELL_EXTIPSEL1_SHIFT * intNo));
203   } else {
204 #if defined(_GPIO_EXTIPSELH_MASK)
205     tmp = intNo - 8;
206 #if defined(_GPIO_EXTIPSELH_EXTIPSEL0_MASK)
207     BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
208                        _GPIO_EXTIPSELH_EXTIPSEL0_MASK
209                        << (_GPIO_EXTIPSELH_EXTIPSEL1_SHIFT * tmp),
210                        (uint32_t)port << (_GPIO_EXTIPSELH_EXTIPSEL1_SHIFT * tmp));
211 #elif defined(_GPIO_EXTIPSELH_EXTIPSEL8_MASK)
212     BUS_RegMaskedWrite(&GPIO->EXTIPSELH,
213                        _GPIO_EXTIPSELH_EXTIPSEL8_MASK
214                        << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp),
215                        (uint32_t)port << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
216 #else
217 #error Invalid GPIO_EXTIPINSELH bit fields
218 #endif
219 #endif /* #if defined(_GPIO_EXTIPSELH_MASK) */
220   }
221 
222 #if defined(_GPIO_EXTIPINSELL_MASK)
223 
224   /* The EXTIPINSELL register controls interrupt 0-7 and EXTIPINSELH controls
225    * interrupt 8-15 of the interrupt/pin number mapping. */
226   if (intNo < 8) {
227     BUS_RegMaskedWrite(&GPIO->EXTIPINSELL,
228                        _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK
229                        << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo),
230                        (uint32_t)((pin % 4) & _GPIO_EXTIPINSELL_EXTIPINSEL0_MASK)
231                        << (_GPIO_EXTIPINSELL_EXTIPINSEL1_SHIFT * intNo));
232   } else {
233 #if defined (_GPIO_EXTIPINSELH_EXTIPINSEL8_MASK)
234     BUS_RegMaskedWrite(&GPIO->EXTIPINSELH,
235                        _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK
236                        << (_GPIO_EXTIPINSELH_EXTIPINSEL9_SHIFT * tmp),
237                        (uint32_t)((pin % 4) & _GPIO_EXTIPINSELH_EXTIPINSEL8_MASK)
238                        << (_GPIO_EXTIPSELH_EXTIPSEL9_SHIFT * tmp));
239 #endif
240 #if defined (_GPIO_EXTIPINSELH_EXTIPINSEL0_MASK)
241     BUS_RegMaskedWrite(&GPIO->EXTIPINSELH,
242                        _GPIO_EXTIPINSELH_EXTIPINSEL0_MASK
243                        << (_GPIO_EXTIPINSELH_EXTIPINSEL1_SHIFT * tmp),
244                        (uint32_t)((pin % 4) & _GPIO_EXTIPINSELH_EXTIPINSEL0_MASK)
245                        << (_GPIO_EXTIPSELH_EXTIPSEL1_SHIFT * tmp));
246 #endif
247   }
248 #endif
249 
250   /* Enable/disable the rising edge interrupt. */
251   BUS_RegBitWrite(&(GPIO->EXTIRISE), intNo, risingEdge);
252 
253   /* Enable/disable the falling edge interrupt. */
254   BUS_RegBitWrite(&(GPIO->EXTIFALL), intNo, fallingEdge);
255 
256   /* Clear any pending interrupt. */
257   GPIO_IntClear(1 << intNo);
258 
259   /* Finally enable/disable interrupt. */
260   BUS_RegBitWrite(&(GPIO->IEN), intNo, enable);
261 }
262 
263 #if _SILICON_LABS_32B_SERIES > 0
264 /***************************************************************************//**
265  * @brief
266  *   Configure EM4WU pins as external level-sensitive interrupts.
267  *
268  * @details
269  *   It is recommended to disable interrupts before configuring the GPIO pin interrupt.
270  *   See @ref GPIO_IntDisable() for more information.
271  *
272  *   The GPIO interrupt handler must be in place before enabling the
273  *   interrupt.
274  *
275  *   Notice that any pending interrupt for the selected interrupt is cleared
276  *   by this function.
277  *
278  * @note
279  *   The selected port/pin must be mapped to an existant EM4WU interrupt.
280  *   Each EM4WU signal is connected to a fixed pin.
281  *   Refer to the Alternate Function Table in the device Datasheet for the
282  *   location of each EM4WU signal. For example, on xG22 device, the interrupt
283  *   of EM4WU6 is fixed to pin PC00.
284  *
285  * @param[in] port
286  *   The port to associate with the @p pin.
287  *
288  * @param[in] pin
289  *   The pin number on the port.
290  *
291  * @param[in] intNo
292  *   The EM4WU interrupt number to trigger.
293  *
294  * @param[in] polarity
295  *   true = Active high level-sensitive interrupt.
296  *   false = Active low level-sensitive interrupt.
297  *
298  * @param[in] enable
299  *   Set to true if the interrupt will be enabled after the configuration is complete.
300  *   False to leave disabled. See @ref GPIO_IntDisable() and @ref GPIO_IntEnable().
301  ******************************************************************************/
GPIO_EM4WUExtIntConfig(GPIO_Port_TypeDef port,unsigned int pin,uint32_t intNo,bool polarity,bool enable)302 void GPIO_EM4WUExtIntConfig(GPIO_Port_TypeDef port,
303                             unsigned int pin,
304                             uint32_t intNo,
305                             bool polarity,
306                             bool enable)
307 {
308   EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
309 
310   // GPIO pin mode set.
311   GPIO_PinModeSet(port, pin, gpioModeInputPullFilter, (unsigned int)!polarity);
312 
313   // Enable EM4WU function and set polarity
314   uint32_t polarityMask = (uint32_t)polarity << (intNo + _GPIO_EM4WUEN_EM4WUEN_SHIFT);
315   uint32_t pinmask =  1UL << (intNo + _GPIO_EM4WUEN_EM4WUEN_SHIFT);
316 
317   GPIO_EM4EnablePinWakeup(pinmask, polarityMask);
318 
319   // Enable EM4WU interrupt
320 #if defined(_SILICON_LABS_32B_SERIES_1)
321   BUS_RegBitWrite(&(GPIO->IEN), intNo + _GPIO_IEN_EM4WU_SHIFT, enable);
322 #elif defined(_SILICON_LABS_32B_SERIES_2_CONFIG_1)
323   BUS_RegBitWrite(&(GPIO->IEN), intNo + _GPIO_IEN_EM4WUIEN_SHIFT, enable);
324 #else
325   BUS_RegBitWrite(&(GPIO->IEN), intNo + _GPIO_IEN_EM4WUIEN0_SHIFT, enable);
326 #endif
327 }
328 #endif
329 
330 /***************************************************************************//**
331  * @brief
332  *   Set the mode for a GPIO pin.
333  *
334  * @param[in] port
335  *   The GPIO port to access.
336  *
337  * @param[in] pin
338  *   The pin number in the port.
339  *
340  * @param[in] mode
341  *   The desired pin mode.
342  *
343  * @param[in] out
344  *   A value to set for the pin in the DOUT register. The DOUT setting is important for
345  *   some input mode configurations to determine the pull-up/down direction.
346  ******************************************************************************/
GPIO_PinModeSet(GPIO_Port_TypeDef port,unsigned int pin,GPIO_Mode_TypeDef mode,unsigned int out)347 void GPIO_PinModeSet(GPIO_Port_TypeDef port,
348                      unsigned int pin,
349                      GPIO_Mode_TypeDef mode,
350                      unsigned int out)
351 {
352   EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
353 
354   /* If disabling a pin, do not modify DOUT to reduce the chance of */
355   /* a glitch/spike (may not be sufficient precaution in all use cases). */
356   if (mode != gpioModeDisabled) {
357     if (out) {
358       GPIO_PinOutSet(port, pin);
359     } else {
360       GPIO_PinOutClear(port, pin);
361     }
362   }
363 
364   /* There are two registers controlling the pins for each port. The MODEL
365    * register controls pins 0-7 and MODEH controls pins 8-15. */
366   if (pin < 8) {
367     // Cast parameter [mode] to 32 bits to fix C99 Undefined Behavior (see SEI CERT C INT34-C)
368     // Compiler assigned 8 bits for enum. Same thing for other branch.
369     BUS_RegMaskedWrite(&(GPIO->P[port].MODEL), 0xFu << (pin * 4), (uint32_t)mode << (pin * 4));
370   } else {
371     BUS_RegMaskedWrite(&(GPIO->P[port].MODEH), 0xFu << ((pin - 8) * 4), (uint32_t)mode << ((pin - 8) * 4));
372   }
373 
374   if (mode == gpioModeDisabled) {
375     if (out) {
376       GPIO_PinOutSet(port, pin);
377     } else {
378       GPIO_PinOutClear(port, pin);
379     }
380   }
381 }
382 
383 /***************************************************************************//**
384  * @brief
385  *   Get the mode for a GPIO pin.
386  *
387  * @param[in] port
388  *   The GPIO port to access.
389  *
390  * @param[in] pin
391  *   The pin number in the port.
392  *
393  * @return
394  *   The pin mode.
395  ******************************************************************************/
GPIO_PinModeGet(GPIO_Port_TypeDef port,unsigned int pin)396 GPIO_Mode_TypeDef GPIO_PinModeGet(GPIO_Port_TypeDef port,
397                                   unsigned int pin)
398 {
399   EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
400 
401   if (pin < 8) {
402     return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEL >> (pin * 4)) & 0xF);
403   } else {
404     return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEH >> ((pin - 8) * 4)) & 0xF);
405   }
406 }
407 
408 #if defined(_GPIO_EM4WUEN_MASK)
409 /**************************************************************************//**
410  * @brief
411  *   Enable GPIO pin wake-up from EM4. When the function exits,
412  *   EM4 mode can be safely entered.
413  *
414  * @note
415  *   It is assumed that the GPIO pin modes are set correctly.
416  *   Valid modes are @ref gpioModeInput and @ref gpioModeInputPull.
417  *
418  * @param[in] pinmask
419  *   A bitmask containing the bitwise logic OR of which GPIO pin(s) to enable.
420  *   See Reference Manuals for a pinmask to the GPIO port/pin mapping.
421  * @param[in] polaritymask
422  *   A bitmask containing the bitwise logic OR of GPIO pin(s) wake-up polarity.
423  *   See Reference Manuals for pinmask-to-GPIO port/pin mapping.
424  *****************************************************************************/
GPIO_EM4EnablePinWakeup(uint32_t pinmask,uint32_t polaritymask)425 void GPIO_EM4EnablePinWakeup(uint32_t pinmask, uint32_t polaritymask)
426 {
427   EFM_ASSERT((pinmask & ~_GPIO_EM4WUEN_MASK) == 0);
428 
429 #if defined(_GPIO_EM4WUPOL_MASK)
430   EFM_ASSERT((polaritymask & ~_GPIO_EM4WUPOL_MASK) == 0);
431   GPIO->EM4WUPOL &= ~pinmask;               /* Set the wakeup polarity. */
432   GPIO->EM4WUPOL |= pinmask & polaritymask;
433 #elif defined(_GPIO_EXTILEVEL_MASK)
434   EFM_ASSERT((polaritymask & ~_GPIO_EXTILEVEL_MASK) == 0);
435   GPIO->EXTILEVEL &= ~pinmask;
436   GPIO->EXTILEVEL |= pinmask & polaritymask;
437 #endif
438   GPIO->EM4WUEN  |= pinmask;                /* Enable wakeup. */
439 
440   GPIO_EM4SetPinRetention(true);            /* Enable the pin retention. */
441 
442 #if defined(_GPIO_CMD_EM4WUCLR_MASK)
443   GPIO->CMD = GPIO_CMD_EM4WUCLR;            /* Clear the wake-up logic. */
444 #else
445   GPIO_IntClear(pinmask);
446 #endif
447 }
448 #endif
449 
450 /** @} (end addtogroup gpio) */
451 
452 #endif /* defined(GPIO_COUNT) && (GPIO_COUNT > 0) */
453