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