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