1 /**
2 * \file
3 *
4 * \brief SAM External Interrupt Driver
5 *
6 * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved.
7 *
8 * \asf_license_start
9 *
10 * \page License
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 *
22 * 3. The name of Atmel may not be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * 4. This software may only be redistributed and used in connection with an
26 * Atmel microcontroller product.
27 *
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 *
40 * \asf_license_stop
41 *
42 */
43 /*
44 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45 */
46 #include <system.h>
47 #include <system_interrupt.h>
48 #include <extint.h>
49 #include <conf_extint.h>
50
51 #if !defined(EXTINT_CLOCK_SOURCE) || defined(__DOXYGEN__)
52 # warning EXTINT_CLOCK_SOURCE is not defined, assuming GCLK_GENERATOR_0.
53
54 /** Configuration option, setting the EIC clock source which can be used for
55 * EIC edge detection or filtering. This option may be overridden in the module
56 * configuration header file \c conf_extint.h.
57 */
58 # define EXTINT_CLOCK_SOURCE GCLK_GENERATOR_0
59 #endif
60
61 /**
62 * \internal
63 * Internal driver device instance struct.
64 */
65 struct _extint_module _extint_dev;
66
67 /**
68 * \brief Determin if the general clock is required
69 *
70 * \param[in] filter_input_signal Filter the raw input signal to prevent noise
71 * \param[in] detection_criteria Edge detection mode to use (\ref extint_detect)
72 */
73 #define _extint_is_gclk_required(filter_input_signal, detection_criteria) \
74 ((filter_input_signal) ? true : (\
75 (EXTINT_DETECT_RISING == (detection_criteria)) ? true : (\
76 (EXTINT_DETECT_FALLING == (detection_criteria)) ? true : (\
77 (EXTINT_DETECT_BOTH == (detection_criteria)) ? true : false))))
78
79 static void _extint_enable(void);
80 static void _extint_disable(void);
81
82 /**
83 * \brief Determines if the hardware module(s) are currently synchronizing to the bus.
84 *
85 * Checks to see if the underlying hardware peripheral module(s) are currently
86 * synchronizing across multiple clock domains to the hardware bus, This
87 * function can be used to delay further operations on a module until such time
88 * that it is ready, to prevent blocking delays for synchronization in the
89 * user application.
90 *
91 * \return Synchronization status of the underlying hardware module(s).
92 *
93 * \retval true If the module synchronization is ongoing
94 * \retval false If the module has completed synchronization
95 */
extint_is_syncing(void)96 static inline bool extint_is_syncing(void)
97 {
98 Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
99
100 for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
101 if (eics[i]->STATUS.reg & EIC_STATUS_SYNCBUSY) {
102 return true;
103 }
104 }
105 return false;
106 }
107 /**
108 * \internal
109 * \brief Initializes and enables the External Interrupt driver.
110 *
111 * Enable the clocks used by External Interrupt driver.
112 *
113 * Resets the External Interrupt driver, resetting all hardware
114 * module registers to their power-on defaults, then enable it for further use.
115 *
116 * Reset the callback list if callback mode is used.
117 *
118 * This function must be called before attempting to use any NMI or standard
119 * external interrupt channel functions.
120 *
121 * \note When SYSTEM module is used, this function will be invoked by
122 * \ref system_init() automatically if the module is included.
123 */
124 void _system_extint_init(void);
_system_extint_init(void)125 void _system_extint_init(void)
126 {
127 Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
128
129 /* Turn on the digital interface clock */
130 system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBA, PM_APBAMASK_EIC);
131
132 /* Configure the generic clock for the module and enable it */
133 struct system_gclk_chan_config gclk_chan_conf;
134 system_gclk_chan_get_config_defaults(&gclk_chan_conf);
135 gclk_chan_conf.source_generator = EXTINT_CLOCK_SOURCE;
136 system_gclk_chan_set_config(EIC_GCLK_ID, &gclk_chan_conf);
137
138 /* Enable the clock anyway, since when needed it will be requested
139 * by External Interrupt driver */
140 system_gclk_chan_enable(EIC_GCLK_ID);
141
142 /* Reset all EIC hardware modules. */
143 for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
144 eics[i]->CTRL.reg |= EIC_CTRL_SWRST;
145 }
146
147 while (extint_is_syncing()) {
148 /* Wait for all hardware modules to complete synchronization */
149 }
150
151 /* Reset the software module */
152 #if EXTINT_CALLBACK_MODE == true
153 /* Clear callback registration table */
154 for (uint8_t j = 0; j < EIC_NUMBER_OF_INTERRUPTS; j++) {
155 _extint_dev.callbacks[j] = NULL;
156 }
157 system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_EIC);
158 #endif
159
160 /* Enables the driver for further use */
161 _extint_enable();
162 }
163
164 /**
165 * \internal
166 * \brief Enables the External Interrupt driver.
167 *
168 * Enables EIC modules.
169 * Registered callback list will not be affected if callback mode is used.
170 */
_extint_enable(void)171 void _extint_enable(void)
172 {
173 Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
174
175 /* Enable all EIC hardware modules. */
176 for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
177 eics[i]->CTRL.reg |= EIC_CTRL_ENABLE;
178 }
179
180 while (extint_is_syncing()) {
181 /* Wait for all hardware modules to complete synchronization */
182 }
183 }
184
185 /**
186 * \internal
187 * \brief Disables the External Interrupt driver.
188 *
189 * Disables EIC modules that were previously started via a call to
190 * \ref _extint_enable().
191 * Registered callback list will not be affected if callback mode is used.
192 */
_extint_disable(void)193 void _extint_disable(void)
194 {
195 Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
196
197 /* Disable all EIC hardware modules. */
198 for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
199 eics[i]->CTRL.reg &= ~EIC_CTRL_ENABLE;
200 }
201
202 while (extint_is_syncing()) {
203 /* Wait for all hardware modules to complete synchronization */
204 }
205 }
206
207 /**
208 * \brief Initializes an External Interrupt channel configuration structure to defaults.
209 *
210 * Initializes a given External Interrupt channel configuration structure to a
211 * set of known default values. This function should be called on all new
212 * instances of these configuration structures before being modified by the
213 * user application.
214 *
215 * The default configuration is as follows:
216 * \li Wake the device if an edge detection occurs whilst in sleep
217 * \li Input filtering disabled
218 * \li Internal pull-up enabled
219 * \li Detect falling edges of a signal
220 *
221 * \param[out] config Configuration structure to initialize to default values
222 */
extint_chan_get_config_defaults(struct extint_chan_conf * const config)223 void extint_chan_get_config_defaults(
224 struct extint_chan_conf *const config)
225 {
226 /* Sanity check arguments */
227 Assert(config);
228
229 /* Default configuration values */
230 config->gpio_pin = 0;
231 config->gpio_pin_mux = 0;
232 config->gpio_pin_pull = EXTINT_PULL_UP;
233 config->wake_if_sleeping = true;
234 config->filter_input_signal = false;
235 config->detection_criteria = EXTINT_DETECT_FALLING;
236 }
237
238 /**
239 * \brief Writes an External Interrupt channel configuration to the hardware module.
240 *
241 * Writes out a given configuration of an External Interrupt channel
242 * configuration to the hardware module. If the channel is already configured,
243 * the new configuration will replace the existing one.
244 *
245 * \param[in] channel External Interrupt channel to configure
246 * \param[in] config Configuration settings for the channel
247
248 */
extint_chan_set_config(const uint8_t channel,const struct extint_chan_conf * const config)249 void extint_chan_set_config(
250 const uint8_t channel,
251 const struct extint_chan_conf *const config)
252 {
253 /* Sanity check arguments */
254 Assert(config);
255 /* Sanity check clock requirements */
256 Assert(!(!system_gclk_gen_is_enabled(EXTINT_CLOCK_SOURCE) &&
257 _extint_is_gclk_required(config->filter_input_signal,
258 config->detection_criteria)));
259
260 struct system_pinmux_config pinmux_config;
261 system_pinmux_get_config_defaults(&pinmux_config);
262
263 pinmux_config.mux_position = config->gpio_pin_mux;
264 pinmux_config.direction = SYSTEM_PINMUX_PIN_DIR_INPUT;
265 pinmux_config.input_pull = (enum system_pinmux_pin_pull)config->gpio_pin_pull;
266 system_pinmux_pin_set_config(config->gpio_pin, &pinmux_config);
267
268 /* Get a pointer to the module hardware instance */
269 Eic *const EIC_module = _extint_get_eic_from_channel(channel);
270
271 uint32_t config_pos = (4 * (channel % 8));
272 uint32_t new_config;
273
274 /* Determine the channel's new edge detection configuration */
275 new_config = (config->detection_criteria << EIC_CONFIG_SENSE0_Pos);
276
277 /* Enable the hardware signal filter if requested in the config */
278 if (config->filter_input_signal) {
279 new_config |= EIC_CONFIG_FILTEN0;
280 }
281
282 /* Clear the existing and set the new channel configuration */
283 EIC_module->CONFIG[channel / 8].reg
284 = (EIC_module->CONFIG[channel / 8].reg &
285 ~((EIC_CONFIG_SENSE0_Msk | EIC_CONFIG_FILTEN0) << config_pos)) |
286 (new_config << config_pos);
287
288 /* Set the channel's new wake up mode setting */
289 if (config->wake_if_sleeping) {
290 EIC_module->WAKEUP.reg |= (1UL << channel);
291 } else {
292 EIC_module->WAKEUP.reg &= ~(1UL << channel);
293 }
294 }
295
296 /**
297 * \brief Writes an External Interrupt NMI channel configuration to the hardware module.
298 *
299 * Writes out a given configuration of an External Interrupt NMI channel
300 * configuration to the hardware module. If the channel is already configured,
301 * the new configuration will replace the existing one.
302 *
303 * \param[in] nmi_channel External Interrupt NMI channel to configure
304 * \param[in] config Configuration settings for the channel
305 *
306 * \returns Status code indicating the success or failure of the request.
307 * \retval STATUS_OK Configuration succeeded
308 * \retval STATUS_ERR_PIN_MUX_INVALID An invalid pinmux value was supplied
309 * \retval STATUS_ERR_BAD_FORMAT An invalid detection mode was requested
310 */
extint_nmi_set_config(const uint8_t nmi_channel,const struct extint_nmi_conf * const config)311 enum status_code extint_nmi_set_config(
312 const uint8_t nmi_channel,
313 const struct extint_nmi_conf *const config)
314 {
315 /* Sanity check arguments */
316 Assert(config);
317 /* Sanity check clock requirements */
318 Assert(!(!system_gclk_gen_is_enabled(EXTINT_CLOCK_SOURCE) &&
319 _extint_is_gclk_required(config->filter_input_signal,
320 config->detection_criteria)));
321
322 struct system_pinmux_config pinmux_config;
323 system_pinmux_get_config_defaults(&pinmux_config);
324
325 pinmux_config.mux_position = config->gpio_pin_mux;
326 pinmux_config.direction = SYSTEM_PINMUX_PIN_DIR_INPUT;
327 pinmux_config.input_pull = SYSTEM_PINMUX_PIN_PULL_UP;
328 pinmux_config.input_pull = (enum system_pinmux_pin_pull)config->gpio_pin_pull;
329 system_pinmux_pin_set_config(config->gpio_pin, &pinmux_config);
330
331 /* Get a pointer to the module hardware instance */
332 Eic *const EIC_module = _extint_get_eic_from_channel(nmi_channel);
333
334 uint32_t new_config;
335
336 /* Determine the NMI's new edge detection configuration */
337 new_config = (config->detection_criteria << EIC_NMICTRL_NMISENSE_Pos);
338
339 /* Enable the hardware signal filter if requested in the config */
340 if (config->filter_input_signal) {
341 new_config |= EIC_NMICTRL_NMIFILTEN;
342 }
343
344 /* Disable EIC and general clock to configure NMI */
345 _extint_disable();
346 system_gclk_chan_disable(EIC_GCLK_ID);
347
348 EIC_module->NMICTRL.reg = new_config;
349
350 /* Enable the general clock and EIC after configure NMI */
351 system_gclk_chan_enable(EIC_GCLK_ID);
352 _extint_enable();
353
354 return STATUS_OK;
355 }
356
357 /**
358 * \brief Enables an External Interrupt event output.
359 *
360 * Enables one or more output events from the External Interrupt module. See
361 * \ref extint_events "here" for a list of events this module supports.
362 *
363 * \note Events cannot be altered while the module is enabled.
364 *
365 * \param[in] events Struct containing flags of events to enable
366 */
extint_enable_events(struct extint_events * const events)367 void extint_enable_events(
368 struct extint_events *const events)
369 {
370 /* Sanity check arguments */
371 Assert(events);
372
373 /* Array of available EICs. */
374 Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
375
376 /* Update the event control register for each physical EIC instance */
377 for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
378 uint32_t event_mask = 0;
379
380 /* Create an enable mask for the current EIC module */
381 for (uint32_t j = 0; j < 32; j++) {
382 if (events->generate_event_on_detect[(32 * i) + j]) {
383 event_mask |= (1UL << j);
384 }
385 }
386
387 /* Enable the masked events */
388 eics[i]->EVCTRL.reg |= event_mask;
389 }
390 }
391
392 /**
393 * \brief Disables an External Interrupt event output.
394 *
395 * Disables one or more output events from the External Interrupt module. See
396 * \ref extint_events "here" for a list of events this module supports.
397 *
398 * \note Events cannot be altered while the module is enabled.
399 *
400 * \param[in] events Struct containing flags of events to disable
401 */
extint_disable_events(struct extint_events * const events)402 void extint_disable_events(
403 struct extint_events *const events)
404 {
405 /* Sanity check arguments */
406 Assert(events);
407
408 /* Array of available EICs. */
409 Eic *const eics[EIC_INST_NUM] = EIC_INSTS;
410
411 /* Update the event control register for each physical EIC instance */
412 for (uint32_t i = 0; i < EIC_INST_NUM; i++) {
413 uint32_t event_mask = 0;
414
415 /* Create a disable mask for the current EIC module */
416 for (uint32_t j = 0; j < 32; j++) {
417 if (events->generate_event_on_detect[(32 * i) + j]) {
418 event_mask |= (1UL << j);
419 }
420 }
421
422 /* Disable the masked events */
423 eics[i]->EVCTRL.reg &= ~event_mask;
424 }
425 }
426