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 #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) {
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 
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 
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 
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 
95  *sys_ticks_per_us = SYS_TICKS_PER_US_XTAL / BIT(div);
96  break;
97  }
98 }
99 
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) {
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 
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 
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 
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 
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 
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,
328 #endif /* HAS_SW_SOCWATCH */
329 
330  return 0;
331 }
332 
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,
342 #endif /* HAS_SW_SOCWATCH */
343 
344  return 0;
345 }
346 
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 
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  */
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
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
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_adc_set_div(const uint16_t div)
Change divider value of ADC clock.
Definition: clk.c:249
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