/* * System clock settings */ static __inline__ void clock_setup(void) { #if (HAS_HYB_XTAL) /* Apply factory settings for Crystal Oscillator stabilization * These settings adjust the trimming value and the counter value * for the Crystal Oscillator */ QM_SCSS_CCU->osc0_cfg1 &= ~OSC0_CFG1_OSC0_FADJ_XTAL_MASK; QM_SCSS_CCU->osc0_cfg1 |= (OSC0_CFG1_OSC0_FADJ_XTAL_DEFAULT << OSC0_CFG1_OSC0_FADJ_XTAL_OFFS); QM_SCSS_CCU->osc0_cfg0 &= ~OSC0_CFG0_OSC0_XTAL_COUNT_VALUE_MASK; QM_SCSS_CCU->osc0_cfg0 |= (OSC0_CFG0_OSC0_XTAL_COUNT_VALUE_DEFAULT << OSC0_CFG0_OSC0_XTAL_COUNT_VALUE_OFFS); #endif /* HAS_HYB_XTAL */ /* * Switch to each silicon oscillator to set up trim data * This sets up the trim codes for the first boot. * This consists of computing the trim code if not available * in non volatile memory and write this results in flash. * * This step is only performed if the shadow region * is not populated. * We rely on the 32MHz trim code to be shadowed to * consider the region populated. * * This can be modified if this policy does not match your * specific requirements. */ if ((QM_FLASH_DATA_TRIM_CODE->osc_trim_32mhz & QM_FLASH_TRIM_PRESENT_MASK) != QM_FLASH_TRIM_PRESENT) { boot_clk_trim_code_setup(); } /* Switch to 32MHz silicon oscillator */ boot_clk_hyb_set_mode(CLK_SYS_HYB_OSC_32MHZ, CLK_SYS_DIV_1); clk_trim_apply(QM_FLASH_DATA_TRIM_CODE->osc_trim_32mhz); }
int clk_sys_set_mode(const clk_sys_mode_t mode, const clk_sys_div_t div) { QM_CHECK(div <= CLK_SYS_DIV_NUM, -EINVAL); QM_CHECK(mode <= CLK_SYS_CRYSTAL_OSC, -EINVAL); uint16_t trim = 0; /* Store system ticks per us */ uint32_t sys_ticks_per_us = 1; /* * Get current settings, clear the clock divisor bits, and clock divider * enable bit. */ uint32_t ccu_sys_clk_ctl = QM_SCSS_CCU->ccu_sys_clk_ctl & CLK_SYS_CLK_DIV_DEF_MASK; /* * Steps: * 1. Enable the new oscillator and wait for it to stabilise. * 2. Switch to the new oscillator * Note on registers: * - QM_OSC0_MODE_SEL: * - asserted: it switches to external crystal oscillator * - not asserted: it switches to silicon oscillator * - QM_CCU_SYS_CLK_SEL: * - asserted: it switches to hybrid (silicon or external) * oscillator * - not asserted: it switches to RTC oscillator * 3. Hybrid oscillator only: apply sysclk divisor * 4. Disable mutually exclusive clock sources. For internal silicon * oscillator is disables the external crystal oscillator and vice * versa. */ switch (mode) { case CLK_SYS_HYB_OSC_32MHZ: case CLK_SYS_HYB_OSC_16MHZ: case CLK_SYS_HYB_OSC_8MHZ: case CLK_SYS_HYB_OSC_4MHZ: /* Calculate the system clock ticks per microsecond * and get the shadowed trim code from the Data Region of Flash. */ if (CLK_SYS_HYB_OSC_32MHZ == mode) { sys_ticks_per_us = SYS_TICKS_PER_US_32MHZ / BIT(div); trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_32mhz; } else if (CLK_SYS_HYB_OSC_16MHZ == mode) { sys_ticks_per_us = SYS_TICKS_PER_US_16MHZ / BIT(div); trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_16mhz; } else if (CLK_SYS_HYB_OSC_8MHZ == mode) { sys_ticks_per_us = SYS_TICKS_PER_US_8MHZ / BIT(div); trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_8mhz; } else { sys_ticks_per_us = SYS_TICKS_PER_US_4MHZ / BIT(div); trim = QM_FLASH_DATA_TRIM_CODE->osc_trim_4mhz; } /* * Apply trim code for the selected mode if this has been * written in the soc_data section. * This is performed in rom on the first boot for each * available frequency. * If not present, something went wrong and trim code * will not be applied. */ if ((trim & QM_FLASH_TRIM_PRESENT_MASK) == QM_FLASH_TRIM_PRESENT) { clk_trim_apply(trim); } /* Select the silicon oscillator frequency */ QM_SCSS_CCU->osc0_cfg1 &= ~OSC0_CFG1_SI_FREQ_SEL_MASK; QM_SCSS_CCU->osc0_cfg1 |= (mode << OSC0_CFG1_SI_FREQ_SEL_OFFS); /* Enable the silicon oscillator */ QM_SCSS_CCU->osc0_cfg1 |= QM_OSC0_EN_SI_OSC; /* Wait for the oscillator to lock */ while (!(QM_SCSS_CCU->osc0_stat1 & QM_OSC0_LOCK_SI)) { }; /* Switch to silicon oscillator mode */ QM_SCSS_CCU->osc0_cfg1 &= ~QM_OSC0_MODE_SEL; /* Set the system clock divider */ QM_SCSS_CCU->ccu_sys_clk_ctl = ccu_sys_clk_ctl | QM_CCU_SYS_CLK_SEL | (div << QM_CCU_SYS_CLK_DIV_OFFSET); /* Disable the crystal oscillator */ QM_SCSS_CCU->osc0_cfg1 &= ~QM_OSC0_EN_CRYSTAL; break; case CLK_SYS_RTC_OSC: /* The RTC oscillator is on by hardware default */ ccu_sys_clk_ctl |= (QM_CCU_RTC_CLK_EN | (div << QM_CCU_SYS_CLK_DIV_OFFSET)); QM_SCSS_CCU->ccu_sys_clk_ctl = (ccu_sys_clk_ctl & ~(QM_CCU_SYS_CLK_SEL)); break; case CLK_SYS_CRYSTAL_OSC: QM_SCSS_CCU->osc0_cfg1 |= QM_OSC0_EN_CRYSTAL; sys_ticks_per_us = SYS_TICKS_PER_US_XTAL / BIT(div); while (!(QM_SCSS_CCU->osc0_stat1 & QM_OSC0_LOCK_XTAL)) { }; QM_SCSS_CCU->osc0_cfg1 |= QM_OSC0_MODE_SEL; QM_SCSS_CCU->ccu_sys_clk_ctl = ccu_sys_clk_ctl | QM_CCU_SYS_CLK_SEL | (div << QM_CCU_SYS_CLK_DIV_OFFSET); QM_SCSS_CCU->osc0_cfg1 &= ~QM_OSC0_EN_SI_OSC; break; } QM_SCSS_CCU->ccu_sys_clk_ctl |= QM_CCU_SYS_CLK_DIV_EN; ticks_per_us = (sys_ticks_per_us > 0 ? sys_ticks_per_us : 1); return 0; }