Intel® Quark™ Microcontroller Software Interface  1.4.0
Intel® Quark™ Microcontroller BSP
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) {
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 
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 
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 
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 
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) {
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 
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 
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 
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 
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 
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,
319 #endif /* HAS_SW_SOCWATCH */
320 
321  return 0;
322 }
323 
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,
333 #endif /* HAS_SW_SOCWATCH */
334  return 0;
335 }
336 
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 
357 {
358  /*
359  * Section 7.2.7 from Quark SE datasheet describes the USB
360  * Clock setup.
361  */
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 
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 
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  */
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
System clock mode type.
Definition: clk.h:51
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
int clk_sys_usb_disable(void)
Disable the USB Clock mode.
Definition: clk.c:386
Real Time Clock Divider = 32768.
Definition: clk.h:113
int clk_rtc_set_div(const clk_rtc_div_t div)
Change divider value of RTC.
Definition: clk.c:306
Frequency altered.
Definition: soc_watch.h:42
SOC register altered.
Definition: soc_watch.h:40
int clk_periph_enable(const clk_periph_t clocks)
Enable clocks for peripherals / registers.
Definition: clk.c:319
Peripheral Clock Divider = 8.
Definition: clk.h:67
uint32_t get_i2c_clk_freq_in_mhz(void)
Get I2C clock frequency in MHz.
Definition: clk.c:381
int clk_gpio_db_set_div(const clk_gpio_db_div_t div)
Change divider value of GPIO debounce clock.
Definition: clk.c:279
Crystal Oscillator Clock.
Definition: clk.h:57
int clk_dma_enable(void)
Enable the DMA clock.
Definition: clk.c:362
Quark D2000 peripherals Enable.
Definition: qm_soc_regs.h:1390
0x018 Perip Clock Gate Ctl.
Definition: soc_watch.h:77
clk_sys_div_t
System clock divider type.
Definition: clk.h:36
16MHz Hybrid Oscillator Clock.
Definition: clk.h:53
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
Peripheral clock divider type.
Definition: clk.h:63
32MHz Hybrid Oscillator Clock.
Definition: clk.h:52
GPIO Clock Debounce Divider = 128.
Definition: clk.h:81
External Crystal Clock Divider = 8.
Definition: clk.h:91
clk_gpio_db_div_t
GPIO clock debounce divider type.
Definition: clk.h:73
clk_rtc_div_t
RTC clock divider type.
Definition: clk.h:97
void clk_sys_udelay(uint32_t microseconds)
Idle loop the processor for at least the value given in microseconds.
Definition: clk.c:352
8MHz Hybrid Oscillator Clock.
Definition: clk.h:54
uint32_t clk_sys_get_ticks_per_us(void)
Get number of system ticks per micro second.
Definition: clk.c:347
int clk_periph_disable(const clk_periph_t clocks)
Disable clocks for peripherals / registers.
Definition: clk.c:333
int clk_periph_set_div(const clk_periph_div_t div)
Change divider value of peripheral clock.
Definition: clk.c:265
int clk_trim_apply(const uint32_t value)
Apply silicon oscillator trim code.
Definition: clk.c:224
int clk_ext_set_div(const clk_ext_div_t div)
Change divider value of external clock.
Definition: clk.c:293
int clk_dma_disable(void)
Disable the DMA clock.
Definition: clk.c:369
Real Time Clock.
Definition: clk.h:56
4MHz Hybrid Oscillator Clock.
Definition: clk.h:55
clk_periph_t
Peripheral clock register map.
Definition: qm_soc_regs.h:1368
clk_ext_div_t
External crystal clock divider type.
Definition: clk.h:87
int clk_sys_usb_enable(void)
Enable the USB Clock mode.
Definition: clk.c:356
Clock Divider = 1.
Definition: clk.h:37