Intel® Quark™ Microcontroller Software Interface 1.4.0 Intel® Quark™ Microcontroller BSP

- Main Page
- Modules
- Data Structures
- Files

- File List

clk.c
1 /*
2 * {% copyright %}
3 */
4
5 #include "clk.h"
6 #include "flash_layout.h"
7 #include "qm_flash.h"
8 #if (!QM_SENSOR) || (UNIT_TEST)
9 #include <x86intrin.h>
10 #endif
11
12 #include "soc_watch.h"
13
14 #if (QM_SENSOR) && (!UNIT_TEST)
15 /* Timestamp counter for Sensor Subsystem is 32bit. */
16 #define get_ticks() __builtin_arc_lr(QM_SS_TSC_BASE + QM_SS_TIMER_COUNT)
17 #elif(QM_SENSOR) && (UNIT_TEST)
18 #define get_ticks() _rdtsc() % ((uint32_t)-1)
19 #else
20 /* 64bit Timestamp counter */
21 #define get_ticks() _rdtsc()
22 #endif
23
24 /* NOTE: Currently user space data / bss section overwrites the ROM data / bss
25 * sections, so anything that is set in the ROM will be obliterated once we jump
26 * into the user app.
27 */
28 static uint32_t ticks_per_us = SYS_TICKS_PER_US_32MHZ;
29
30 /* Set up flash timings according to the target sysclk frequency.
31 *
32 * By POR prefetcher is disabled.
33 * Drivers do not expect the pre-fetcher to be enabled,
34 * therefore this function does assume the prefetcher is always turned off.
35 */
36 static void apply_flash_timings(uint32_t sys_ticks_per_us)
37 {
38 uint32_t flash;
39
40 for (flash = QM_FLASH_0; flash < QM_FLASH_NUM; flash++) {
41 if (sys_ticks_per_us <= SYS_TICKS_PER_US_4MHZ) {
42 /*
43 * QM_FLASH_CLK_SLOW enables 0 wait states
44 * for flash accesses.
45 */
46 QM_FLASH[flash]->tmg_ctrl |= QM_FLASH_CLK_SLOW;
47 QM_FLASH[flash]->tmg_ctrl &= ~QM_FLASH_WAIT_STATE_MASK;
48 } else if (sys_ticks_per_us <= SYS_TICKS_PER_US_16MHZ) {
49 QM_FLASH[flash]->tmg_ctrl &= ~QM_FLASH_CLK_SLOW;
50 /*
51 * READ_WAIT_STATE_L has an integrated +1 which
52 * results as 1 wait state for 8MHz and 16MHz.
53 */
54 QM_FLASH[flash]->tmg_ctrl &= ~QM_FLASH_WAIT_STATE_MASK;
55 } else {
56 QM_FLASH[flash]->tmg_ctrl &= ~QM_FLASH_CLK_SLOW;
57 /*
58 * READ_WAIT_STATE_L has an integrated +1 which
59 * results as 2 wait states for 32MHz.
60 */
61 QM_FLASH[flash]->tmg_ctrl =
62 (QM_FLASH[flash]->tmg_ctrl &
63 ~QM_FLASH_WAIT_STATE_MASK) |
64 (1 << QM_FLASH_WAIT_STATE_OFFSET);
65 }
66 }
67 }
68
69 /*
70 * Compute the system clock ticks per microsecond and get the shadowed trim code
71 * from the Data Region of Flash.
72 */
73 static void clk_sys_compute_new_frequency(clk_sys_mode_t mode,
74 clk_sys_div_t div,
75 uint32_t *sys_ticks_per_us,
76 uint16_t *trim)
77 {
78 switch (mode) {
79 case CLK_SYS_HYB_OSC_32MHZ:
80 *sys_ticks_per_us = SYS_TICKS_PER_US_32MHZ / BIT(div);
81 *trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_32mhz;
82 break;
83
84 case CLK_SYS_HYB_OSC_16MHZ:
85 *sys_ticks_per_us = SYS_TICKS_PER_US_16MHZ / BIT(div);
86 *trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_16mhz;
87 break;
88
89 case CLK_SYS_HYB_OSC_8MHZ:
90 *sys_ticks_per_us = SYS_TICKS_PER_US_8MHZ / BIT(div);
91 *trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_8mhz;
92 break;
93
94 case CLK_SYS_HYB_OSC_4MHZ:
95 *sys_ticks_per_us = SYS_TICKS_PER_US_4MHZ / BIT(div);
96 *trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_4mhz;
97 break;
98
99 case CLK_SYS_RTC_OSC:
100 *sys_ticks_per_us = 1;
101 break;
102
103 case CLK_SYS_CRYSTAL_OSC:
104 *sys_ticks_per_us = SYS_TICKS_PER_US_XTAL / BIT(div);
105 break;
106 }
107 }
108
109 int clk_sys_set_mode(const clk_sys_mode_t mode, const clk_sys_div_t div)
110 {
111 QM_CHECK(div < CLK_SYS_DIV_NUM, -EINVAL);
112 QM_CHECK(mode <= CLK_SYS_CRYSTAL_OSC, -EINVAL);
113 uint16_t trim = 0;
114
115 /* Store system ticks per us */
116 uint32_t sys_ticks_per_us = 1;
117
118 /*
119 * Get current settings, clear the clock divisor bits, and clock divider
120 * enable bit.
121 */
122 uint32_t ccu_sys_clk_ctl =
123 QM_SCSS_CCU->ccu_sys_clk_ctl & CLK_SYS_CLK_DIV_DEF_MASK;
124
125 /* Compute new frequency parameters. */
126 clk_sys_compute_new_frequency(mode, div, &sys_ticks_per_us, &trim);
127
128 /*
129 * Changing sysclk frequency requires flash settings (mainly
130 * wait states) to be realigned so as to avoid timing violations.
131 * During clock switching, we change flash timings to the
132 * most conservative settings (supporting up to 32MHz).
133 */
134 apply_flash_timings(SYS_TICKS_PER_US_32MHZ);
135
136 /*
137 * Steps:
138 * 1. Enable the new oscillator and wait for it to stabilise.
139 * 2. Switch to the new oscillator
140 * Note on registers:
141 * - QM_OSC0_MODE_SEL:
142 * - asserted: it switches to external crystal oscillator
143 * - not asserted: it switches to silicon oscillator
144 * - QM_CCU_SYS_CLK_SEL:
145 * - asserted: it switches to hybrid (silicon or external)
146 * oscillator
147 * - not asserted: it switches to RTC oscillator
148 * 3. Hybrid oscillator only: apply sysclk divisor
149 * 4. Disable mutually exclusive clock sources. For internal silicon
150 * oscillator is disables the external crystal oscillator and vice
151 * versa.
152 */
153 switch (mode) {
154 case CLK_SYS_HYB_OSC_32MHZ:
155 case CLK_SYS_HYB_OSC_16MHZ:
156 case CLK_SYS_HYB_OSC_8MHZ:
157 case CLK_SYS_HYB_OSC_4MHZ:
158 /*
159 * Apply trim code for the selected mode if this has been
160 * written in the soc_data section.
161 * This is performed in rom on the first boot for each
162 * available frequency.
163 * If not present, something went wrong and trim code
164 * will not be applied.
165 */
166 if ((trim & QM_FLASH_TRIM_PRESENT_MASK) ==
167 QM_FLASH_TRIM_PRESENT) {
168 clk_trim_apply(trim);
169 }
170 /* Select the silicon oscillator frequency */
171 QM_SCSS_CCU->osc0_cfg1 &= ~OSC0_CFG1_SI_FREQ_SEL_MASK;
172 QM_SCSS_CCU->osc0_cfg1 |= (mode << OSC0_CFG1_SI_FREQ_SEL_OFFS);
173 /* Enable the silicon oscillator */
174 QM_SCSS_CCU->osc0_cfg1 |= QM_OSC0_EN_SI_OSC;
175 /* Wait for the oscillator to lock */
176 while (!(QM_SCSS_CCU->osc0_stat1 & QM_OSC0_LOCK_SI)) {
177 };
178 /* Switch to silicon oscillator mode */
179 QM_SCSS_CCU->osc0_cfg1 &= ~QM_OSC0_MODE_SEL;
180 /* Set the system clock divider */
181 QM_SCSS_CCU->ccu_sys_clk_ctl =
182 ccu_sys_clk_ctl | QM_CCU_SYS_CLK_SEL |
183 (div << QM_CCU_SYS_CLK_DIV_OFFSET);
184 /* Disable the crystal oscillator */
185 QM_SCSS_CCU->osc0_cfg1 &= ~QM_OSC0_EN_CRYSTAL;
186 break;
187
188 case CLK_SYS_RTC_OSC:
189 /* The RTC oscillator is on by hardware default */
190 ccu_sys_clk_ctl |=
191 (QM_CCU_RTC_CLK_EN | (div << QM_CCU_SYS_CLK_DIV_OFFSET));
192
193 QM_SCSS_CCU->ccu_sys_clk_ctl =
194 (ccu_sys_clk_ctl & ~(QM_CCU_SYS_CLK_SEL));
195 break;
196
197 case CLK_SYS_CRYSTAL_OSC:
198 QM_SCSS_CCU->osc0_cfg1 |= QM_OSC0_EN_CRYSTAL;
199 sys_ticks_per_us = SYS_TICKS_PER_US_XTAL / BIT(div);
200 while (!(QM_SCSS_CCU->osc0_stat1 & QM_OSC0_LOCK_XTAL)) {
201 };
202 QM_SCSS_CCU->osc0_cfg1 |= QM_OSC0_MODE_SEL;
203 QM_SCSS_CCU->ccu_sys_clk_ctl =
204 ccu_sys_clk_ctl | QM_CCU_SYS_CLK_SEL |
205 (div << QM_CCU_SYS_CLK_DIV_OFFSET);
206 QM_SCSS_CCU->osc0_cfg1 &= ~QM_OSC0_EN_SI_OSC;
207 break;
208 }
209
210 QM_SCSS_CCU->ccu_sys_clk_ctl |= QM_CCU_SYS_CLK_DIV_EN;
211 ticks_per_us = (sys_ticks_per_us > 0 ? sys_ticks_per_us : 1);
212
213 /*
214 * Apply flash timings for the new clock settings.
215 */
216 apply_flash_timings(sys_ticks_per_us);
217
218 /* Log any clock changes. */
219 SOC_WATCH_LOG_EVENT(SOCW_EVENT_FREQ, 0);
220 return 0;
221 }
222
223 int clk_trim_read(uint32_t *const value)
224 {
225 QM_CHECK(NULL != value, -EINVAL);
226
227 *value = (QM_SCSS_CCU->osc0_cfg1 & OSC0_CFG1_FTRIMOTP_MASK) >>
228 OSC0_CFG1_FTRIMOTP_OFFS;
229
230 return 0;
231 }
232
233 int clk_trim_apply(const uint32_t value)
234 {
235 /* Enable trim mode */
236 QM_SCSS_CCU->osc0_cfg0 |= BIT(1);
237
238 /* Apply trim code */
239 QM_SCSS_CCU->osc0_cfg1 &= ~OSC0_CFG1_FTRIMOTP_MASK;
240 QM_SCSS_CCU->osc0_cfg1 |=
241 (value << OSC0_CFG1_FTRIMOTP_OFFS) & OSC0_CFG1_FTRIMOTP_MASK;
242
243 /*
244 * Recommended wait time after setting up the trim code
245 * is 200us. Minimum wait time is 100us.
246 * The delay is running from of the silicon oscillator
247 * which is been trimmed. This induces a lack of precision
248 * in the delay.
249 */
250 clk_sys_udelay(200);
251
252 /* Disable trim mode */
253 QM_SCSS_CCU->osc0_cfg0 &= ~BIT(1);
254
255 return 0;
256 }
257
258 int clk_periph_set_div(const clk_periph_div_t div)
259 {
260 QM_CHECK(div <= CLK_PERIPH_DIV_8, -EINVAL);
261
262 QM_SCSS_CCU->ccu_periph_clk_div_ctl0 =
263 (div << QM_CCU_PERIPH_PCLK_DIV_OFFSET);
264 /* CLK Div en bit must be written from 0 -> 1 to apply new value */
265 QM_SCSS_CCU->ccu_periph_clk_div_ctl0 |= QM_CCU_PERIPH_PCLK_DIV_EN;
266
267 return 0;
268 }
269
270 int clk_gpio_db_set_div(const clk_gpio_db_div_t div)
271 {
272 QM_CHECK(div <= CLK_GPIO_DB_DIV_128, -EINVAL);
273
274 uint32_t reg =
275 QM_SCSS_CCU->ccu_gpio_db_clk_ctl & CLK_GPIO_DB_DIV_DEF_MASK;
276 reg |= (div << QM_CCU_GPIO_DB_DIV_OFFSET);
277 QM_SCSS_CCU->ccu_gpio_db_clk_ctl = reg;
278 /* CLK Div en bit must be written from 0 -> 1 to apply new value */
279 QM_SCSS_CCU->ccu_gpio_db_clk_ctl |= QM_CCU_GPIO_DB_CLK_DIV_EN;
280
281 return 0;
282 }
283
284 int clk_ext_set_div(const clk_ext_div_t div)
285 {
286 QM_CHECK(div <= CLK_EXT_DIV_8, -EINVAL);
287
288 uint32_t reg = QM_SCSS_CCU->ccu_ext_clock_ctl & CLK_EXTERN_DIV_DEF_MASK;
289 reg |= (div << QM_CCU_EXTERN_DIV_OFFSET);
290 QM_SCSS_CCU->ccu_ext_clock_ctl = reg;
291 /* CLK Div en bit must be written from 0 -> 1 to apply new value */
292 QM_SCSS_CCU->ccu_ext_clock_ctl |= QM_CCU_EXT_CLK_DIV_EN;
293
294 return 0;
295 }
296
297 int clk_rtc_set_div(const clk_rtc_div_t div)
298 {
299 QM_CHECK(div <= CLK_RTC_DIV_32768, -EINVAL);
300
301 uint32_t reg = QM_SCSS_CCU->ccu_sys_clk_ctl & CLK_RTC_DIV_DEF_MASK;
302 reg |= (div << QM_CCU_RTC_CLK_DIV_OFFSET);
303 QM_SCSS_CCU->ccu_sys_clk_ctl = reg;
304 /* CLK Div en bit must be written from 0 -> 1 to apply new value */
305 QM_SCSS_CCU->ccu_sys_clk_ctl |= QM_CCU_RTC_CLK_DIV_EN;
306
307 return 0;
308 }
309
310 int clk_periph_enable(const clk_periph_t clocks)
311 {
312 QM_CHECK(clocks <= CLK_PERIPH_ALL, -EINVAL);
313
314 QM_SCSS_CCU->ccu_periph_clk_gate_ctl |= clocks;
315
316 #if (HAS_SW_SOCWATCH)
317 SOC_WATCH_LOG_EVENT(SOCW_EVENT_REGISTER,
318 SOCW_REG_CCU_PERIPH_CLK_GATE_CTL);
319 #endif /* HAS_SW_SOCWATCH */
320
321 return 0;
322 }
323
324 int clk_periph_disable(const clk_periph_t clocks)
325 {
326 QM_CHECK(clocks <= CLK_PERIPH_ALL, -EINVAL);
327
328 QM_SCSS_CCU->ccu_periph_clk_gate_ctl &= ~clocks;
329
330 #if (HAS_SW_SOCWATCH)
331 SOC_WATCH_LOG_EVENT(SOCW_EVENT_REGISTER,
332 SOCW_REG_CCU_PERIPH_CLK_GATE_CTL);
333 #endif /* HAS_SW_SOCWATCH */
334 return 0;
335 }
336
337 uint32_t clk_sys_get_ticks_per_us(void)
338 {
339 return ticks_per_us;
340 }
341
342 void clk_sys_udelay(uint32_t microseconds)
343 {
344 uint32_t timeout = ticks_per_us * microseconds;
345 #if (QM_SENSOR)
346 uint32_t tsc_start;
347 #else
348 unsigned long long tsc_start;
349 #endif
350 tsc_start = get_ticks();
351 /* We need to wait until timeout system clock ticks has occurred. */
352 while (get_ticks() - tsc_start < timeout) {
353 }
354 }
355
356 int clk_sys_usb_enable(void)
357 {
358 /*
359 * Section 7.2.7 from Quark SE datasheet describes the USB
360 * Clock setup.
361 */
362 clk_sys_set_mode(CLK_SYS_CRYSTAL_OSC, CLK_SYS_DIV_1);
363
364 /* Enable the USB Clock. */
365 QM_SCSS_CCU->ccu_mlayer_ahb_ctl |= QM_CCU_USB_CLK_EN;
366
367 /* Set up the PLL. */
368 QM_USB_PLL_CFG0 = QM_USB_PLL_CFG0_DEFAULT | QM_USB_PLL_PDLD;
369
370 /* Let's have at most 50ms timeout for the PLL lock. */
371 int timeout = 5;
372
373 /* Wait for the PLL lock. */
374 while (!(QM_USB_PLL_CFG0 & QM_USB_PLL_LOCK) && timeout) {
375 clk_sys_udelay(10000); /* delay for 10ms. */
376 timeout--;
377 }
378
379 if (!timeout) {
380 return -EIO;
381 }
382
383 return 0;
384 }
385
386 int clk_sys_usb_disable(void)
387 {
388 /* Disable the USB Clock. */
389 QM_SCSS_CCU->ccu_mlayer_ahb_ctl &= ~QM_CCU_USB_CLK_EN;
390
391 /* Disable the PLL. */
392 QM_USB_PLL_CFG0 &= ~QM_USB_PLL_PDLD;
393
394 /* Let's have at most 50ms timeout for the PLL lock. */
395 int timeout = 5;
396
397 /* Wait for the PLL to unlock. */
398 while ((QM_USB_PLL_CFG0 & QM_USB_PLL_LOCK) && timeout) {
399 clk_sys_udelay(10000); /* delay for 10ms. */
400 timeout--;
401 }
402
403 if (!timeout) {
404 return -EIO;
405 }
406
407 return 0;
408 }
409
410 int clk_dma_enable(void)
411 {
412 QM_SCSS_CCU->ccu_mlayer_ahb_ctl |= QM_CCU_DMA_CLK_EN;
413
414 return 0;
415 }
416
417 int clk_dma_disable(void)
418 {
419 QM_SCSS_CCU->ccu_mlayer_ahb_ctl &= ~QM_CCU_DMA_CLK_EN;
420
421 return 0;
422 }
423
424 /**
425 * Get I2C clock frequency in MHz.
426 *
427 * @return [uint32_t] I2C freq_in_mhz.
428 */
429 uint32_t get_i2c_clk_freq_in_mhz(void)
430 {
431 return clk_sys_get_ticks_per_us() >>
432 ((QM_SCSS_CCU->ccu_periph_clk_div_ctl0 &
433 CLK_PERIPH_DIV_DEF_MASK) >>
434 QM_CCU_PERIPH_PCLK_DIV_OFFSET);
435 }
clk_sys_mode_t
clk_sys_mode_t
System clock mode type.
Definition: clk.h:51
clk_sys_set_mode
int clk_sys_set_mode(const clk_sys_mode_t mode, const clk_sys_div_t div)
Set clock mode and divisor.
Definition: clk.c:100
clk_sys_usb_disable
int clk_sys_usb_disable(void)
Disable the USB Clock mode.
Definition: clk.c:386
CLK_RTC_DIV_32768
Real Time Clock Divider = 32768.
Definition: clk.h:113
clk_rtc_set_div
int clk_rtc_set_div(const clk_rtc_div_t div)
Change divider value of RTC.
Definition: clk.c:306
SOCW_EVENT_FREQ
Frequency altered.
Definition: soc_watch.h:42
SOCW_EVENT_REGISTER
SOC register altered.
Definition: soc_watch.h:40
clk_periph_enable
int clk_periph_enable(const clk_periph_t clocks)
Enable clocks for peripherals / registers.
Definition: clk.c:319
CLK_PERIPH_DIV_8
Peripheral Clock Divider = 8.
Definition: clk.h:67
get_i2c_clk_freq_in_mhz
uint32_t get_i2c_clk_freq_in_mhz(void)
Get I2C clock frequency in MHz.
Definition: clk.c:381
clk_gpio_db_set_div
int clk_gpio_db_set_div(const clk_gpio_db_div_t div)
Change divider value of GPIO debounce clock.
Definition: clk.c:279
CLK_SYS_CRYSTAL_OSC
Crystal Oscillator Clock.
Definition: clk.h:57
clk_dma_enable
int clk_dma_enable(void)
Enable the DMA clock.
Definition: clk.c:362
CLK_PERIPH_ALL
Quark D2000 peripherals Enable.
Definition: qm_soc_regs.h:1390
SOCW_REG_CCU_PERIPH_CLK_GATE_CTL
0x018 Perip Clock Gate Ctl.
Definition: soc_watch.h:77
clk_sys_div_t
clk_sys_div_t
System clock divider type.
Definition: clk.h:36
CLK_SYS_HYB_OSC_16MHZ
16MHz Hybrid Oscillator Clock.
Definition: clk.h:53
clk_trim_read
int clk_trim_read(uint32_t *const value)
Read the silicon oscillator trim code for the current frequency.
Definition: clk.c:214
clk_periph_div_t
clk_periph_div_t
Peripheral clock divider type.
Definition: clk.h:63
CLK_SYS_HYB_OSC_32MHZ
32MHz Hybrid Oscillator Clock.
Definition: clk.h:52
CLK_GPIO_DB_DIV_128
GPIO Clock Debounce Divider = 128.
Definition: clk.h:81
CLK_EXT_DIV_8
External Crystal Clock Divider = 8.
Definition: clk.h:91
clk_gpio_db_div_t
clk_gpio_db_div_t
GPIO clock debounce divider type.
Definition: clk.h:73
clk_rtc_div_t
clk_rtc_div_t
RTC clock divider type.
Definition: clk.h:97
clk_sys_udelay
void clk_sys_udelay(uint32_t microseconds)
Idle loop the processor for at least the value given in microseconds.
Definition: clk.c:352
CLK_SYS_HYB_OSC_8MHZ
8MHz Hybrid Oscillator Clock.
Definition: clk.h:54
clk_sys_get_ticks_per_us
uint32_t clk_sys_get_ticks_per_us(void)
Get number of system ticks per micro second.
Definition: clk.c:347
clk_periph_disable
int clk_periph_disable(const clk_periph_t clocks)
Disable clocks for peripherals / registers.
Definition: clk.c:333
clk_periph_set_div
int clk_periph_set_div(const clk_periph_div_t div)
Change divider value of peripheral clock.
Definition: clk.c:265
clk_trim_apply
int clk_trim_apply(const uint32_t value)
Apply silicon oscillator trim code.
Definition: clk.c:224
clk_ext_set_div
int clk_ext_set_div(const clk_ext_div_t div)
Change divider value of external clock.
Definition: clk.c:293
clk_dma_disable
int clk_dma_disable(void)
Disable the DMA clock.
Definition: clk.c:369
CLK_SYS_RTC_OSC
Real Time Clock.
Definition: clk.h:56
CLK_SYS_HYB_OSC_4MHZ
4MHz Hybrid Oscillator Clock.
Definition: clk.h:55
clk_periph_t
clk_periph_t
Peripheral clock register map.
Definition: qm_soc_regs.h:1368
clk_ext_div_t
clk_ext_div_t
External crystal clock divider type.
Definition: clk.h:87
clk_sys_usb_enable
int clk_sys_usb_enable(void)
Enable the USB Clock mode.
Definition: clk.c:356
CLK_SYS_DIV_1
Clock Divider = 1.
Definition: clk.h:37