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                        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                        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                        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                        ((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                        ((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                        ((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 /***************************************************************************//**
264  * @brief
265  *   Set the mode for a GPIO pin.
266  *
267  * @param[in] port
268  *   The GPIO port to access.
269  *
270  * @param[in] pin
271  *   The pin number in the port.
272  *
273  * @param[in] mode
274  *   The desired pin mode.
275  *
276  * @param[in] out
277  *   A value to set for the pin in the DOUT register. The DOUT setting is important for
278  *   some input mode configurations to determine the pull-up/down direction.
279  ******************************************************************************/
GPIO_PinModeSet(GPIO_Port_TypeDef port,unsigned int pin,GPIO_Mode_TypeDef mode,unsigned int out)280 void GPIO_PinModeSet(GPIO_Port_TypeDef port,
281                      unsigned int pin,
282                      GPIO_Mode_TypeDef mode,
283                      unsigned int out)
284 {
285   EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
286 
287   /* If disabling a pin, do not modify DOUT to reduce the chance of */
288   /* a glitch/spike (may not be sufficient precaution in all use cases). */
289   if (mode != gpioModeDisabled) {
290     if (out) {
291       GPIO_PinOutSet(port, pin);
292     } else {
293       GPIO_PinOutClear(port, pin);
294     }
295   }
296 
297   /* There are two registers controlling the pins for each port. The MODEL
298    * register controls pins 0-7 and MODEH controls pins 8-15. */
299   if (pin < 8) {
300     GPIO->P[port].MODEL = (GPIO->P[port].MODEL & ~(0xFu << (pin * 4)))
301                           | (mode << (pin * 4));
302   } else {
303     GPIO->P[port].MODEH = (GPIO->P[port].MODEH & ~(0xFu << ((pin - 8) * 4)))
304                           | (mode << ((pin - 8) * 4));
305   }
306 
307   if (mode == gpioModeDisabled) {
308     if (out) {
309       GPIO_PinOutSet(port, pin);
310     } else {
311       GPIO_PinOutClear(port, pin);
312     }
313   }
314 }
315 
316 /***************************************************************************//**
317  * @brief
318  *   Get the mode for a GPIO pin.
319  *
320  * @param[in] port
321  *   The GPIO port to access.
322  *
323  * @param[in] pin
324  *   The pin number in the port.
325  *
326  * @return
327  *   The pin mode.
328  ******************************************************************************/
GPIO_PinModeGet(GPIO_Port_TypeDef port,unsigned int pin)329 GPIO_Mode_TypeDef GPIO_PinModeGet(GPIO_Port_TypeDef port,
330                                   unsigned int pin)
331 {
332   EFM_ASSERT(GPIO_PORT_PIN_VALID(port, pin));
333 
334   if (pin < 8) {
335     return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEL >> (pin * 4)) & 0xF);
336   } else {
337     return (GPIO_Mode_TypeDef) ((GPIO->P[port].MODEH >> ((pin - 8) * 4)) & 0xF);
338   }
339 }
340 
341 #if defined(_GPIO_EM4WUEN_MASK)
342 /**************************************************************************//**
343  * @brief
344  *   Enable GPIO pin wake-up from EM4. When the function exits,
345  *   EM4 mode can be safely entered.
346  *
347  * @note
348  *   It is assumed that the GPIO pin modes are set correctly.
349  *   Valid modes are @ref gpioModeInput and @ref gpioModeInputPull.
350  *
351  * @param[in] pinmask
352  *   A bitmask containing the bitwise logic OR of which GPIO pin(s) to enable.
353  *   See Reference Manuals for a pinmask to the GPIO port/pin mapping.
354  * @param[in] polaritymask
355  *   A bitmask containing the bitwise logic OR of GPIO pin(s) wake-up polarity.
356  *   See Reference Manuals for pinmask-to-GPIO port/pin mapping.
357  *****************************************************************************/
GPIO_EM4EnablePinWakeup(uint32_t pinmask,uint32_t polaritymask)358 void GPIO_EM4EnablePinWakeup(uint32_t pinmask, uint32_t polaritymask)
359 {
360   EFM_ASSERT((pinmask & ~_GPIO_EM4WUEN_MASK) == 0);
361 
362 #if defined(_GPIO_EM4WUPOL_MASK)
363   EFM_ASSERT((polaritymask & ~_GPIO_EM4WUPOL_MASK) == 0);
364   GPIO->EM4WUPOL &= ~pinmask;               /* Set the wakeup polarity. */
365   GPIO->EM4WUPOL |= pinmask & polaritymask;
366 #elif defined(_GPIO_EXTILEVEL_MASK)
367   EFM_ASSERT((polaritymask & ~_GPIO_EXTILEVEL_MASK) == 0);
368   GPIO->EXTILEVEL &= ~pinmask;
369   GPIO->EXTILEVEL |= pinmask & polaritymask;
370 #endif
371   GPIO->EM4WUEN  |= pinmask;                /* Enable wakeup. */
372 
373   GPIO_EM4SetPinRetention(true);            /* Enable the pin retention. */
374 
375 #if defined(_GPIO_CMD_EM4WUCLR_MASK)
376   GPIO->CMD = GPIO_CMD_EM4WUCLR;            /* Clear the wake-up logic. */
377 #else
378   GPIO_IntClear(pinmask);
379 #endif
380 }
381 #endif
382 
383 /** @} (end addtogroup gpio) */
384 
385 #endif /* defined(GPIO_COUNT) && (GPIO_COUNT > 0) */
386