diff options
Diffstat (limited to 'chip/g/clock.c')
-rw-r--r-- | chip/g/clock.c | 350 |
1 files changed, 34 insertions, 316 deletions
diff --git a/chip/g/clock.c b/chip/g/clock.c index 72ed2ca820..098c6a4aa7 100644 --- a/chip/g/clock.c +++ b/chip/g/clock.c @@ -5,327 +5,45 @@ #include "clock.h" #include "registers.h" - -/* Clock initialization taken from some example code */ - -#define RESOLUTION 12 -#define LOAD_VAL (0x1 << 11) -#define MAX_TRIM (7*16) - -static void switch_osc_to_xtl(void); - -static void clock_on_xo0(void) -{ - /* turn on xo0 clock */ - /* don't know which control word it might be in */ -#ifdef GC_PMU_PERICLKSET0_DXO0_MASK - GR_PMU_PERICLKSET0 = GC_PMU_PERICLKSET0_DXO0_MASK; -#endif - -#ifdef GC_PMU_PERICLKSET1_DXO0_MASK - GR_PMU_PERICLKSET1 = GC_PMU_PERICLKSET1_DXO0_MASK; -#endif -} - -/* Converts an integer setting to the RC trim code format (7, 4-bit values) */ -static unsigned val_to_trim_code(unsigned val) -{ - unsigned base = val / 7; - unsigned mod = val % 7; - unsigned code = 0x0; - int digit; - - /* Increasing count from right to left */ - for (digit = 0; digit < 7; digit++) { - /* Check for mod */ - if (digit <= mod) - code |= ((base & 0xF) << 4 * digit); - else - code |= (((base - 1) & 0xF) << 4 * digit); - } - - return code; -} - -static unsigned calib_rc_trim(void) -{ - unsigned size, iter; - signed mid; - signed diff; - - /* Switch to crystal for calibration. This should work since we are - * technically on an uncalibrated RC trim clock. */ - switch_osc_to_xtl(); - - clock_on_xo0(); - - /* Clear the HOLD signal on dxo */ - GR_XO_OSC_CLRHOLD = GC_XO_OSC_CLRHOLD_RC_TRIM_MASK; - - /* Reset RC calibration counters */ - GR_XO_OSC_RC_CAL_RSTB = 0x0; - GR_XO_OSC_RC_CAL_RSTB = 0x1; - - /* Write the LOAD val */ - GR_XO_OSC_RC_CAL_LOAD = LOAD_VAL; - - /* Begin binary search */ - mid = 0; - size = MAX_TRIM / 2; - for (iter = 0; iter <= 7; iter++) { - /* Set the trim value */ - GR_XO_OSC_RC = val_to_trim_code(mid) << GC_XO_OSC_RC_TRIM_LSB; - - /* Do a calibration */ - GR_XO_OSC_RC_CAL_START = 0x1; - - /* NOTE: There is a small race condition because of the delay - * in dregfile. The start doesn't actually appear for 2 clock - * cycles after the write. So, poll until done goes low. */ - while (GR_XO_OSC_RC_CAL_DONE) - ; - - /* Wait until it's done */ - while (!GR_XO_OSC_RC_CAL_DONE) - ; - - /* Check the counter value */ - diff = LOAD_VAL - GR_XO_OSC_RC_CAL_COUNT; - - /* Test to see whether we are still outside of our desired - * resolution */ - if ((diff < -RESOLUTION) || (diff > RESOLUTION)) - mid = (diff > 0) ? (mid - size / 2) : (mid + size / 2); - - size = (size + 1) >> 1; /* round up before division */ - } - - /* Set the final trim value */ - GR_XO_OSC_RC = (val_to_trim_code(mid) << GC_XO_OSC_RC_TRIM_LSB) | - (0x1 << GC_XO_OSC_RC_EN_LSB); - - /* Set the HOLD signal on dxo */ - GR_XO_OSC_SETHOLD = GC_XO_OSC_SETHOLD_RC_TRIM_MASK; - - /* Switch back to the RC trim now that we are calibrated */ - GR_PMU_OSC_HOLD_CLR = 0x1; /* make sure the hold signal is clear */ - GR_PMU_OSC_SELECT = GC_PMU_OSC_SELECT_RC_TRIM; - /* Make sure the hold signal is set for future power downs */ - GR_PMU_OSC_HOLD_SET = 0x1; - - return mid; -} - -static void switch_osc_to_rc_trim(void) -{ - unsigned trimmed; - unsigned saved_trim, fuse_trim, default_trim; - unsigned trim_code; - - /* check which clock we are running on */ - unsigned osc_sel = GR_PMU_OSC_SELECT_STAT; - - if (osc_sel == GC_PMU_OSC_SELECT_RC_TRIM) { - /* already using the rc_trim so nothing to do here */ - /* make sure the hold signal is set for future power downs */ - GR_PMU_OSC_HOLD_SET = 0x1; - return; - } - - /* Turn on DXO clock so we can write in the trim code in */ - clock_on_xo0(); - - /* disable the RC_TRIM Clock */ - REG_WRITE_MLV(GR_PMU_OSC_CTRL, - GC_PMU_OSC_CTRL_RC_TRIM_READYB_MASK, - GC_PMU_OSC_CTRL_RC_TRIM_READYB_LSB, 1); - - /* power up the clock if not already powered up */ - GR_PMU_CLRDIS = 1 << GC_PMU_SETDIS_RC_TRIM_LSB; - - /* Try to find the trim code */ - saved_trim = GR_XO_OSC_RC_STATUS; - fuse_trim = GR_PMU_FUSE_RD_RC_OSC_26MHZ; - default_trim = GR_XO_OSC_RC; - - /* Check for the trim code in the always-on domain before looking at - * the fuse */ - if (saved_trim & GC_XO_OSC_RC_STATUS_EN_MASK) { - trim_code = (saved_trim & GC_XO_OSC_RC_STATUS_TRIM_MASK) - >> GC_XO_OSC_RC_STATUS_TRIM_LSB; - trimmed = 1; - } else if (fuse_trim & GC_PMU_FUSE_RD_RC_OSC_26MHZ_EN_MASK) { - trim_code = (fuse_trim & GC_PMU_FUSE_RD_RC_OSC_26MHZ_TRIM_MASK) - >> GC_PMU_FUSE_RD_RC_OSC_26MHZ_TRIM_LSB; - trimmed = 1; - } else { - trim_code = (default_trim & GC_XO_OSC_RC_TRIM_MASK) - >> GC_XO_OSC_RC_TRIM_LSB; - trimmed = 0; - } - - /* Write the trim code to dxo */ - if (trimmed) { - /* clear the hold signal */ - GR_XO_OSC_CLRHOLD = 1 << GC_XO_OSC_CLRHOLD_RC_TRIM_LSB; - GR_XO_OSC_RC = (trim_code << GC_XO_OSC_RC_TRIM_LSB) | - ((trimmed & 0x1) << GC_XO_OSC_RC_EN_LSB); - /* set the hold signal */ - GR_XO_OSC_SETHOLD = 1 << GC_XO_OSC_SETHOLD_RC_TRIM_LSB; - } - - /* enable the RC_TRIM Clock */ - REG_WRITE_MLV(GR_PMU_OSC_CTRL, GC_PMU_OSC_CTRL_RC_TRIM_READYB_MASK, - GC_PMU_OSC_CTRL_RC_TRIM_READYB_LSB, 0); - - /* Switch the select signal */ - GR_PMU_OSC_HOLD_CLR = 0x1; /* make sure the hold signal is clear */ - GR_PMU_OSC_SELECT = GC_PMU_OSC_SELECT_RC_TRIM; - /* make sure the hold signal is set for future power downs */ - GR_PMU_OSC_HOLD_SET = 0x1; - - /* If we didn't find a valid trim code, then we need to calibrate */ - if (!trimmed) - calib_rc_trim(); - /* We saved the trim code and went back to the RC trim inside - * calib_rc_trim */ -} - -static void switch_osc_to_xtl(void) -{ - unsigned int saved_trim, fuse_trim, trim_code, final_trim; - unsigned int fsm_status, max_trim; - unsigned int fsm_done; - /* check which clock we are running on */ - unsigned int osc_sel = GR_PMU_OSC_SELECT_STAT; - - if (osc_sel == GC_PMU_OSC_SELECT_XTL) { - /* already using the crystal so nothing to do here */ - /* make sure the hold signal is set for future power downs */ - GR_PMU_OSC_HOLD_SET = 0x1; - return; - } - - if (osc_sel == GC_PMU_OSC_SELECT_RC) - /* RC untrimmed clock. We must go through the trimmed clock - * first to avoid glitching */ - switch_osc_to_rc_trim(); - - /* disable the XTL Clock */ - REG_WRITE_MLV(GR_PMU_OSC_CTRL, GC_PMU_OSC_CTRL_XTL_READYB_MASK, - GC_PMU_OSC_CTRL_XTL_READYB_LSB, 1); - - /* power up the clock if not already powered up */ - GR_PMU_CLRDIS = 1 << GC_PMU_SETDIS_XTL_LSB; - - /* Try to find the trim code */ - trim_code = 0; - saved_trim = GR_XO_OSC_XTL_TRIM_STAT; - fuse_trim = GR_PMU_FUSE_RD_XTL_OSC_26MHZ; - - /* Check for the trim code in the always-on domain before looking at - * the fuse */ - if (saved_trim & GC_XO_OSC_XTL_TRIM_STAT_EN_MASK) { - /* nothing to do */ - /* trim_code = (saved_trim & GR_XO_OSC_XTL_TRIM_STAT_CODE_MASK) - >> GR_XO_OSC_XTL_TRIM_STAT_CODE_LSB; */ - /* print_trickbox_message("XTL TRIM CODE FOUND IN 3P3"); */ - } else if (fuse_trim & GC_PMU_FUSE_RD_XTL_OSC_26MHZ_EN_MASK) { - /* push the fuse trim code as the saved trim code */ - /* print_trickbox_message("XTL TRIM CODE FOUND IN FUSE"); */ - trim_code = (fuse_trim & GC_PMU_FUSE_RD_XTL_OSC_26MHZ_TRIM_MASK) - >> GC_PMU_FUSE_RD_XTL_OSC_26MHZ_TRIM_LSB; - /* make sure the hold signal is clear */ - GR_XO_OSC_CLRHOLD = 0x1 << GC_XO_OSC_CLRHOLD_XTL_LSB; - GR_XO_OSC_XTL_TRIM = - (trim_code << GC_XO_OSC_XTL_TRIM_CODE_LSB) | - (0x1 << GC_XO_OSC_XTL_TRIM_EN_LSB); - } else - /* print_trickbox_message("XTL TRIM CODE NOT FOUND"); */ - ; - - /* Run the crystal FSM to calibrate the crystal trim */ - fsm_done = GR_XO_OSC_XTL_FSM; - if (fsm_done & GC_XO_OSC_XTL_FSM_DONE_MASK) { - /* If FSM done is high, it means we already ran it so let's not - * run it again */ - /* DO NOTHING */ - } else { - GR_XO_OSC_XTL_FSM_EN = 0x0; /* reset FSM */ - GR_XO_OSC_XTL_FSM_EN = GC_XO_OSC_XTL_FSM_EN_KEY; - while (!(fsm_done & GC_XO_OSC_XTL_FSM_DONE_MASK)) - fsm_done = GR_XO_OSC_XTL_FSM; - } - - /* Check the status and final trim value */ - max_trim = (GR_XO_OSC_XTL_FSM_CFG & GC_XO_OSC_XTL_FSM_CFG_TRIM_MAX_MASK) - >> GC_XO_OSC_XTL_FSM_CFG_TRIM_MAX_LSB; - final_trim = (fsm_done & GC_XO_OSC_XTL_FSM_TRIM_MASK) - >> GC_XO_OSC_XTL_FSM_TRIM_LSB; - fsm_status = (fsm_done & GC_XO_OSC_XTL_FSM_STATUS_MASK) - >> GC_XO_OSC_XTL_FSM_STATUS_LSB; - - /* Check status bit and trim value */ - if (fsm_status) { - if (final_trim >= max_trim) - /* print_trickbox_error("ERROR: XTL FSM status was - high, but final XTL trim is greater than or equal to - max trim"); */ - ; - } else { - if (final_trim != max_trim) - /* print_trickbox_error("ERROR: XTL FSM status was low, - but final XTL trim does not equal max trim"); */ - ; - } - - /* save the trim for future powerups */ - /* make sure the hold signal is clear (may have already been cleared) */ - GR_XO_OSC_CLRHOLD = 0x1 << GC_XO_OSC_CLRHOLD_XTL_LSB; - GR_XO_OSC_XTL_TRIM = - (final_trim << GC_XO_OSC_XTL_TRIM_CODE_LSB) | - (0x1 << GC_XO_OSC_XTL_TRIM_EN_LSB); - /* make sure the hold signal is set for future power downs */ - GR_XO_OSC_SETHOLD = 0x1 << GC_XO_OSC_SETHOLD_XTL_LSB; - - /* enable the XTL Clock */ - REG_WRITE_MLV(GR_PMU_OSC_CTRL, GC_PMU_OSC_CTRL_XTL_READYB_MASK, - GC_PMU_OSC_CTRL_XTL_READYB_LSB, 0); - - /* Switch the select signal */ - GR_PMU_OSC_HOLD_CLR = 0x1; /* make sure the hold signal is clear */ - GR_PMU_OSC_SELECT = GC_PMU_OSC_SELECT_XTL; - /* make sure the hold signal is set for future power downs */ - GR_PMU_OSC_HOLD_SET = 0x1; -} +#include "pmu.h" void clock_init(void) { - /* - * TODO(crosbug.com/p/33813): The following comment was in the example - * code, but the function that's called doesn't match what it says. - * Investigate further. - */ - - /* Switch to crystal clock since RC clock not accurate enough */ - switch_osc_to_rc_trim(); + pmu_clock_en(PERIPH_PERI0); + pmu_clock_en(PERIPH_TIMEHS0); + pmu_clock_en(PERIPH_TIMEHS1); + pmu_clock_switch_xo(); } void clock_enable_module(enum module_id module, int enable) { - if (module != MODULE_UART) - return; - - /* don't know which control word it might be in */ -#ifdef GC_PMU_PERICLKSET0_DUART0_LSB - REG_WRITE_MLV(GR_PMU_PERICLKSET0, - GC_PMU_PERICLKSET0_DUART0_MASK, - GC_PMU_PERICLKSET0_DUART0_LSB, enable); -#endif - -#ifdef GR_PMU_PERICLKSET1_DUART0_LSB - REG_WRITE_MLV(GR_PMU_PERICLKSET1, - GC_PMU_PERICLKSET1_DUART0_MASK, - GC_PMU_PERICLKSET0_DUART1_LSB, enable); -#endif + pmu_clock_func clock_func; + clock_func = (enable) ? pmu_clock_en : pmu_clock_dis; + + switch (module) { + case MODULE_UART: + clock_func(PERIPH_UART0); + break; + case MODULE_I2C: + clock_func(PERIPH_I2C0); + clock_func(PERIPH_I2C1); + break; + case MODULE_SPI_MASTER: + clock_func(PERIPH_SPI); + break; + case MODULE_SPI: + clock_func(PERIPH_SPS); + break; + case MODULE_USB: + clock_func(PERIPH_USB0); + clock_func(PERIPH_USB0_USB_PHY); + pmu_enable_clock_doubler(); + break; + case MODULE_PMU: + clock_func(PERIPH_PMU); + break; + default: + break; + } + return; } |