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