1 /**
2  * \file
3  *
4  * \brief SAM D20 Generic Clock 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 
47 #include <gclk.h>
48 #include <clock.h>
49 #include <system_interrupt.h>
50 
51 /**
52  * \brief Determines if the hardware module(s) are currently synchronizing to the bus.
53  *
54  * Checks to see if the underlying hardware peripheral module(s) are currently
55  * synchronizing across multiple clock domains to the hardware bus, This
56  * function can be used to delay further operations on a module until such time
57  * that it is ready, to prevent blocking delays for synchronization in the
58  * user application.
59  *
60  * \return Synchronization status of the underlying hardware module(s).
61  *
62  * \retval false if the module has completed synchronization
63  * \retval true if the module synchronization is ongoing
64  */
system_gclk_is_syncing(void)65 static inline bool system_gclk_is_syncing(void)
66 {
67 	if (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY){
68 		return true;
69 	}
70 
71 	return false;
72 }
73 
74 /**
75  * \brief Initializes the GCLK driver.
76  *
77  * Initializes the Generic Clock module, disabling and resetting all active
78  * Generic Clock Generators and Channels to their power-on default values.
79  */
system_gclk_init(void)80 void system_gclk_init(void)
81 {
82 	/* Turn on the digital interface clock */
83 	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBA, PM_APBAMASK_GCLK);
84 
85 	/* Software reset the module to ensure it is re-initialized correctly */
86 	GCLK->CTRL.reg = GCLK_CTRL_SWRST;
87 	while (GCLK->CTRL.reg & GCLK_CTRL_SWRST) {
88 		/* Wait for reset to complete */
89 	}
90 }
91 
92 /**
93  * \brief Writes a Generic Clock Generator configuration to the hardware module.
94  *
95  * Writes out a given configuration of a Generic Clock Generator configuration
96  * to the hardware module.
97  *
98  * \note Changing the clock source on the fly (on a running
99  *       generator) can take additional time if the clock source is configured
100  *       to only run on-demand (ONDEMAND bit is set) and it is not currently
101  *       running (no peripheral is requesting the clock source). In this case
102  *       the GCLK will request the new clock while still keeping a request to
103  *       the old clock source until the new clock source is ready.
104  *
105  * \note This function will not start a generator that is not already running;
106  *       to start the generator, call \ref system_gclk_gen_enable()
107  *       after configuring a generator.
108  *
109  * \param[in] generator  Generic Clock Generator index to configure
110  * \param[in] config     Configuration settings for the generator
111  */
system_gclk_gen_set_config(const uint8_t generator,struct system_gclk_gen_config * const config)112 void system_gclk_gen_set_config(
113 		const uint8_t generator,
114 		struct system_gclk_gen_config *const config)
115 {
116 	/* Sanity check arguments */
117 	Assert(config);
118 
119 	/* Cache new register configurations to minimize sync requirements. */
120 	uint32_t new_genctrl_config = (generator << GCLK_GENCTRL_ID_Pos);
121 	uint32_t new_gendiv_config  = (generator << GCLK_GENDIV_ID_Pos);
122 
123 	/* Select the requested source clock for the generator */
124 	new_genctrl_config |= config->source_clock << GCLK_GENCTRL_SRC_Pos;
125 
126 	/* Configure the clock to be either high or low when disabled */
127 	if (config->high_when_disabled) {
128 		new_genctrl_config |= GCLK_GENCTRL_OOV;
129 	}
130 
131 	/* Configure if the clock output to I/O pin should be enabled. */
132 	if (config->output_enable) {
133 		new_genctrl_config |= GCLK_GENCTRL_OE;
134 	}
135 
136 	/* Set division factor */
137 	if (config->division_factor > 1) {
138 		/* Check if division is a power of two */
139 		if (((config->division_factor & (config->division_factor - 1)) == 0)) {
140 			/* Determine the index of the highest bit set to get the
141 			 * division factor that must be loaded into the division
142 			 * register */
143 
144 			uint32_t div2_count = 0;
145 
146 			uint32_t mask;
147 			for (mask = (1UL << 1); mask < config->division_factor;
148 						mask <<= 1) {
149 				div2_count++;
150 			}
151 
152 			/* Set binary divider power of 2 division factor */
153 			new_gendiv_config  |= div2_count << GCLK_GENDIV_DIV_Pos;
154 			new_genctrl_config |= GCLK_GENCTRL_DIVSEL;
155 		} else {
156 			/* Set integer division factor */
157 
158 			new_gendiv_config  |=
159 					(config->division_factor) << GCLK_GENDIV_DIV_Pos;
160 
161 			/* Enable non-binary division with increased duty cycle accuracy */
162 			new_genctrl_config |= GCLK_GENCTRL_IDC;
163 		}
164 
165 	}
166 
167 	/* Enable or disable the clock in standby mode */
168 	if (config->run_in_standby) {
169 		new_genctrl_config |= GCLK_GENCTRL_RUNSTDBY;
170 	}
171 
172 	while (system_gclk_is_syncing()) {
173 		/* Wait for synchronization */
174 	};
175 
176 	system_interrupt_enter_critical_section();
177 
178 	/* Select the correct generator */
179 	*((uint8_t*)&GCLK->GENDIV.reg) = generator;
180 
181 	/* Write the new generator configuration */
182 	while (system_gclk_is_syncing()) {
183 		/* Wait for synchronization */
184 	};
185 	GCLK->GENDIV.reg  = new_gendiv_config;
186 
187 	while (system_gclk_is_syncing()) {
188 		/* Wait for synchronization */
189 	};
190 	GCLK->GENCTRL.reg = new_genctrl_config | (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN);
191 
192 	system_interrupt_leave_critical_section();
193 }
194 
195 /**
196  * \brief Enables a Generic Clock Generator that was previously configured.
197  *
198  * Starts the clock generation of a Generic Clock Generator that was previously
199  * configured via a call to \ref system_gclk_gen_set_config().
200  *
201  * \param[in] generator  Generic Clock Generator index to enable
202  */
system_gclk_gen_enable(const uint8_t generator)203 void system_gclk_gen_enable(
204 		const uint8_t generator)
205 {
206 	while (system_gclk_is_syncing()) {
207 		/* Wait for synchronization */
208 	};
209 
210 	system_interrupt_enter_critical_section();
211 
212 	/* Select the requested generator */
213 	*((uint8_t*)&GCLK->GENCTRL.reg) = generator;
214 	while (system_gclk_is_syncing()) {
215 		/* Wait for synchronization */
216 	};
217 
218 	/* Enable generator */
219 	GCLK->GENCTRL.reg |= GCLK_GENCTRL_GENEN;
220 
221 	system_interrupt_leave_critical_section();
222 }
223 
224 /**
225  * \brief Disables a Generic Clock Generator that was previously enabled.
226  *
227  * Stops the clock generation of a Generic Clock Generator that was previously
228  * started via a call to \ref system_gclk_gen_enable().
229  *
230  * \param[in] generator  Generic Clock Generator index to disable
231  */
system_gclk_gen_disable(const uint8_t generator)232 void system_gclk_gen_disable(
233 		const uint8_t generator)
234 {
235 	while (system_gclk_is_syncing()) {
236 		/* Wait for synchronization */
237 	};
238 
239 	system_interrupt_enter_critical_section();
240 
241 	/* Select the requested generator */
242 	*((uint8_t*)&GCLK->GENCTRL.reg) = generator;
243 	while (system_gclk_is_syncing()) {
244 		/* Wait for synchronization */
245 	};
246 
247 	/* Disable generator */
248 	GCLK->GENCTRL.reg &= ~GCLK_GENCTRL_GENEN;
249 	while (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN) {
250 		/* Wait for clock to become disabled */
251 	}
252 
253 	system_interrupt_leave_critical_section();
254 }
255 
256 /**
257  * \brief Determins if the specified Generic Clock Generator is enabled.
258  *
259  * \param[in] generator  Generic Clock Generator index to check
260  *
261  * \return The enabled status.
262  * \retval true The Generic Clock Generator is enabled
263  * \retval false The Generic Clock Generator is disabled
264  */
system_gclk_gen_is_enabled(const uint8_t generator)265 bool system_gclk_gen_is_enabled(
266 		const uint8_t generator)
267 {
268 	bool enabled;
269 
270 	system_interrupt_enter_critical_section();
271 
272 	/* Select the requested generator */
273 	*((uint8_t*)&GCLK->GENCTRL.reg) = generator;
274 	/* Obtain the enabled status */
275 	enabled = (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN);
276 
277 	system_interrupt_leave_critical_section();
278 
279 	return enabled;
280 }
281 
282 /**
283  * \brief Retrieves the clock frequency of a Generic Clock generator.
284  *
285  * Determines the clock frequency (in Hz) of a specified Generic Clock
286  * generator, used as a source to a Generic Clock Channel module.
287  *
288  * \param[in] generator  Generic Clock Generator index
289  *
290  * \return The frequency of the generic clock generator, in Hz.
291  */
system_gclk_gen_get_hz(const uint8_t generator)292 uint32_t system_gclk_gen_get_hz(
293 		const uint8_t generator)
294 {
295 	while (system_gclk_is_syncing()) {
296 		/* Wait for synchronization */
297 	};
298 
299 	system_interrupt_enter_critical_section();
300 
301 	/* Select the appropriate generator */
302 	*((uint8_t*)&GCLK->GENCTRL.reg) = generator;
303 	while (system_gclk_is_syncing()) {
304 		/* Wait for synchronization */
305 	};
306 
307 	/* Get the frequency of the source connected to the GCLK generator */
308 	uint32_t gen_input_hz = system_clock_source_get_hz(
309 			(enum system_clock_source)GCLK->GENCTRL.bit.SRC);
310 
311 	*((uint8_t*)&GCLK->GENCTRL.reg) = generator;
312 
313 	uint8_t divsel = GCLK->GENCTRL.bit.DIVSEL;
314 
315 	/* Select the appropriate generator division register */
316 	*((uint8_t*)&GCLK->GENDIV.reg) = generator;
317 	while (system_gclk_is_syncing()) {
318 		/* Wait for synchronization */
319 	};
320 
321 	uint32_t divider = GCLK->GENDIV.bit.DIV;
322 
323 	system_interrupt_leave_critical_section();
324 
325 	/* Check if the generator is using fractional or binary division */
326 	if (!divsel && divider > 1) {
327 		gen_input_hz /= divider;
328 	} else if (divsel) {
329 		gen_input_hz >>= (divider+1);
330 	}
331 
332 	return gen_input_hz;
333 }
334 
335 /**
336  * \brief Writes a Generic Clock configuration to the hardware module.
337  *
338  * Writes out a given configuration of a Generic Clock configuration to the
339  * hardware module. If the clock is currently running, it will be stopped.
340  *
341  * \note Once called the clock will not be running; to start the clock,
342  *       call \ref system_gclk_chan_enable() after configuring a clock channel.
343  *
344  * \param[in] channel   Generic Clock channel to configure
345  * \param[in] config    Configuration settings for the clock
346  *
347  */
system_gclk_chan_set_config(const uint8_t channel,struct system_gclk_chan_config * const config)348 void system_gclk_chan_set_config(
349 		const uint8_t channel,
350 		struct system_gclk_chan_config *const config)
351 {
352 	/* Sanity check arguments */
353 	Assert(config);
354 
355 	/* Cache the new config to reduce sync requirements */
356 	uint32_t new_clkctrl_config = (channel << GCLK_CLKCTRL_ID_Pos);
357 
358 	/* Select the desired generic clock generator */
359 	new_clkctrl_config |= config->source_generator << GCLK_CLKCTRL_GEN_Pos;
360 
361 	/* Disable generic clock channel */
362 	system_gclk_chan_disable(channel);
363 
364 	/* Write the new configuration */
365 	GCLK->CLKCTRL.reg = new_clkctrl_config;
366 }
367 
368 /**
369  * \brief Enables a Generic Clock that was previously configured.
370  *
371  * Starts the clock generation of a Generic Clock that was previously
372  * configured via a call to \ref system_gclk_chan_set_config().
373  *
374  * \param[in] channel   Generic Clock channel to enable
375  */
system_gclk_chan_enable(const uint8_t channel)376 void system_gclk_chan_enable(
377 		const uint8_t channel)
378 {
379 	system_interrupt_enter_critical_section();
380 
381 	/* Select the requested generator channel */
382 	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
383 
384 	/* Enable the generic clock */
385 	GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_CLKEN;
386 
387 	system_interrupt_leave_critical_section();
388 }
389 
390 /**
391  * \brief Disables a Generic Clock that was previously enabled.
392  *
393  * Stops the clock generation of a Generic Clock that was previously started
394  * via a call to \ref system_gclk_chan_enable().
395  *
396  * \param[in] channel  Generic Clock channel to disable
397  */
system_gclk_chan_disable(const uint8_t channel)398 void system_gclk_chan_disable(
399 		const uint8_t channel)
400 {
401 	system_interrupt_enter_critical_section();
402 
403 	/* Select the requested generator channel */
404 	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
405 
406 	/* Sanity check WRTLOCK */
407 	Assert(!GCLK->CLKCTRL.bit.WRTLOCK);
408 
409 	/* Switch to known-working source so that the channel can be disabled */
410 	uint32_t prev_gen_id = GCLK->CLKCTRL.bit.GEN;
411 	GCLK->CLKCTRL.bit.GEN = 0;
412 
413 	/* Disable the generic clock */
414 	GCLK->CLKCTRL.reg &= ~GCLK_CLKCTRL_CLKEN;
415 	while (GCLK->CLKCTRL.reg & GCLK_CLKCTRL_CLKEN) {
416 		/* Wait for clock to become disabled */
417 	}
418 
419 	/* Restore previous configured clock generator */
420 	GCLK->CLKCTRL.bit.GEN = prev_gen_id;
421 
422 	system_interrupt_leave_critical_section();
423 }
424 
425 /**
426  * \brief Determins if the specified Generic Clock channel is enabled
427  *
428  * \param[in] channel  Generic Clock Channel index
429  *
430  * \return The enabled status.
431  * \retval true The Generic Clock channel is enabled
432  * \retval false The Generic Clock channel is disabled
433  */
system_gclk_chan_is_enabled(const uint8_t channel)434 bool system_gclk_chan_is_enabled(
435 		const uint8_t channel)
436 {
437 	bool enabled;
438 
439 	system_interrupt_enter_critical_section();
440 
441 	/* Select the requested generic clock channel */
442 	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
443 	enabled = GCLK->CLKCTRL.bit.CLKEN;
444 
445 	system_interrupt_leave_critical_section();
446 
447 	return enabled;
448 }
449 
450 /**
451  * \brief Locks a Generic Clock channel from further configuration writes.
452  *
453  * Locks a generic clock channel from further configuration writes. It is only
454  * possible to unlock the channel configuration through a power on reset.
455  *
456  * \param[in] channel   Generic Clock channel to enable
457  */
system_gclk_chan_lock(const uint8_t channel)458 void system_gclk_chan_lock(
459 		const uint8_t channel)
460 {
461 	system_interrupt_enter_critical_section();
462 
463 	/* Select the requested generator channel */
464 	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
465 
466 	/* Lock the generic clock */
467 	GCLK->CLKCTRL.reg |= GCLK_CLKCTRL_WRTLOCK | GCLK_CLKCTRL_CLKEN;
468 
469 	system_interrupt_leave_critical_section();
470 }
471 
472 /**
473  * \brief Determins if the specified Generic Clock channel is locked.
474  *
475  * \param[in] channel  Generic Clock Channel index
476  *
477  * \return The lock status.
478  * \retval true The Generic Clock channel is locked
479  * \retval false The Generic Clock channel is not locked
480  */
system_gclk_chan_is_locked(const uint8_t channel)481 bool system_gclk_chan_is_locked(
482 		const uint8_t channel)
483 {
484 	bool locked;
485 
486 	system_interrupt_enter_critical_section();
487 
488 	/* Select the requested generic clock channel */
489 	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
490 	locked = GCLK->CLKCTRL.bit.WRTLOCK;
491 
492 	system_interrupt_leave_critical_section();
493 
494 	return locked;
495 }
496 
497 /**
498  * \brief Retrieves the clock frequency of a Generic Clock channel.
499  *
500  * Determines the clock frequency (in Hz) of a specified Generic Clock
501  * channel, used as a source to a device peripheral module.
502  *
503  * \param[in] channel  Generic Clock Channel index
504  *
505  * \return The frequency of the generic clock channel, in Hz.
506  */
system_gclk_chan_get_hz(const uint8_t channel)507 uint32_t system_gclk_chan_get_hz(
508 		const uint8_t channel)
509 {
510 	uint8_t gen_id;
511 
512 	system_interrupt_enter_critical_section();
513 
514 	/* Select the requested generic clock channel */
515 	*((uint8_t*)&GCLK->CLKCTRL.reg) = channel;
516 	gen_id = GCLK->CLKCTRL.bit.GEN;
517 
518 	system_interrupt_leave_critical_section();
519 
520 	/* Return the clock speed of the associated GCLK generator */
521 	return system_gclk_gen_get_hz(gen_id);
522 }
523