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