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