mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-06 15:01:35 +00:00
cr50: Added PMU driver
- Porting from cosmo code base. - Support clock initialization BRANCH=none BUG=chrome-os-partner:33813 TEST="make buildall -j; Verified on RevA1 Chip" Change-Id: I59e2bb133968d408acde44a3082e1b3b8f4bbbff Signed-off-by: Sheng-Liang Song <ssl@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/236394 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
This commit is contained in:
committed by
ChromeOS Commit Bot
parent
3f061f1864
commit
c12181d9af
@@ -17,4 +17,5 @@ CPPFLAGS+= -DGC_REVISION="$(ver_str)"
|
||||
|
||||
# Required chip modules
|
||||
chip-y=clock.o gpio.o hwtimer.o jtag.o system.o uart.o
|
||||
chip-y+= pmu.o
|
||||
chip-$(CONFIG_WATCHDOG)+=watchdog.o
|
||||
|
||||
348
chip/g/clock.c
348
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;
|
||||
pmu_clock_func clock_func;
|
||||
clock_func = (enable) ? pmu_clock_en : pmu_clock_dis;
|
||||
|
||||
/* 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
|
||||
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;
|
||||
}
|
||||
|
||||
619
chip/g/pmu.c
Normal file
619
chip/g/pmu.c
Normal file
@@ -0,0 +1,619 @@
|
||||
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "pmu.h"
|
||||
#include "task.h"
|
||||
|
||||
/*
|
||||
* RC Trim constants
|
||||
*/
|
||||
#define RCTRIM_RESOLUTION (12)
|
||||
#define RCTRIM_LOAD_VAL (1 << 11)
|
||||
#define RCTRIM_RANGE_MAX (7 * 7)
|
||||
#define RCTRIM_RANGE_MIN (-8 * 7)
|
||||
#define RCTRIM_RANGE (RCTRIM_RANGE_MAX - RCTRIM_RANGE_MIN + 1)
|
||||
|
||||
/*
|
||||
* Enable peripheral clock
|
||||
* @param perih Peripheral from @ref uint32_t
|
||||
*/
|
||||
void pmu_clock_en(uint32_t periph)
|
||||
{
|
||||
if (periph <= 31)
|
||||
GR_PMU_PERICLKSET0 = (1 << periph);
|
||||
else
|
||||
GR_PMU_PERICLKSET1 = (1 << (periph - 32));
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable peripheral clock
|
||||
* @param perih Peripheral from @ref uint32_t
|
||||
*/
|
||||
void pmu_clock_dis(uint32_t periph)
|
||||
{
|
||||
if (periph <= 31)
|
||||
GR_PMU_PERICLKCLR0 = (1 << periph);
|
||||
else
|
||||
GR_PMU_PERICLKCLR1 = (1 << (periph - 32));
|
||||
}
|
||||
|
||||
/*
|
||||
* Peripheral reset
|
||||
* @param periph Peripheral from @ref uint32_t
|
||||
*/
|
||||
void pmu_peripheral_rst(uint32_t periph)
|
||||
{
|
||||
/* Reset high */
|
||||
if (periph <= 31)
|
||||
GR_PMU_RST0 = 1 << periph;
|
||||
else
|
||||
GR_PMU_RST1 = 1 << (periph - 32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal helper to convert value to trim code
|
||||
*/
|
||||
static uint32_t _pmu_value_to_trim(uint32_t val)
|
||||
{
|
||||
uint32_t base = val / 7;
|
||||
uint32_t mod = val % 7;
|
||||
uint32_t code = 0x0;
|
||||
uint32_t digit;
|
||||
|
||||
/* Increasing count from right to left */
|
||||
for (digit = 0; digit < 7; digit++) {
|
||||
if (digit < mod)
|
||||
code |= (((base + 1) & 0xF) << (4 * digit));
|
||||
else
|
||||
code |= ((base & 0xF) << (4 * digit));
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the RC calibration counters
|
||||
* This must be used otherwise the counters may get stuck
|
||||
*/
|
||||
static uint32_t _pmu_run_rc_counters(uint32_t load_val, uint32_t trim_val)
|
||||
{
|
||||
uint32_t trim_code;
|
||||
|
||||
/* Convert value to trim code */
|
||||
trim_code = _pmu_value_to_trim(trim_val);
|
||||
|
||||
/* Set the trim value */
|
||||
GWRITE_FIELD(XO, OSC_RC, TRIM, trim_code);
|
||||
|
||||
/* Reset counters */
|
||||
GR_XO_OSC_RC_CAL_RSTB = 0x0;
|
||||
GR_XO_OSC_RC_CAL_RSTB = 0x1;
|
||||
|
||||
/* Load */
|
||||
GR_XO_OSC_RC_CAL_LOAD = load_val;
|
||||
|
||||
/* Do calibration */
|
||||
GR_XO_OSC_RC_CAL_START = 0x1;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
;
|
||||
|
||||
/* Calculate the difference */
|
||||
return GR_XO_OSC_RC_CAL_LOAD - GR_XO_OSC_RC_CAL_COUNT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calibrate RC trim
|
||||
*/
|
||||
uint32_t pmu_calibrate_rc_trim(void)
|
||||
{
|
||||
uint32_t size, iter;
|
||||
uint32_t mid;
|
||||
uint32_t diff;
|
||||
|
||||
/*
|
||||
* Switch to crystal for calibration
|
||||
* This should work since we are on an uncalibrated RC trim clock
|
||||
*/
|
||||
pmu_clock_switch_xo();
|
||||
|
||||
/* Clear the HOLD signal on dxo */
|
||||
GR_XO_OSC_CLRHOLD = GC_XO_OSC_CLRHOLD_RC_TRIM_MASK;
|
||||
|
||||
/* Clear EN bit while iterating through codes */
|
||||
GWRITE_FIELD(XO, OSC_RC, EN, 0);
|
||||
|
||||
/* Begin binary search */
|
||||
mid = RCTRIM_RANGE_MAX - (RCTRIM_RANGE / 2);
|
||||
size = RCTRIM_RANGE / 2;
|
||||
for (iter = 0; iter < 8; iter++) {
|
||||
/* Run the counters */
|
||||
diff = _pmu_run_rc_counters(RCTRIM_LOAD_VAL, mid);
|
||||
|
||||
/*
|
||||
* Test to see whether we are still outside of
|
||||
* our desired resolution
|
||||
*/
|
||||
if ((diff < -RCTRIM_RESOLUTION)
|
||||
|| (diff > RCTRIM_RESOLUTION)) {
|
||||
if (diff > 0)
|
||||
mid -= size / 2;
|
||||
else
|
||||
mid += size / 2;
|
||||
}
|
||||
|
||||
/* Move to next range, round up */
|
||||
size = (size + 1) >> 1;
|
||||
}
|
||||
|
||||
/* Set the final trim value, set EN bit to lock in the code */
|
||||
GR_XO_OSC_RC = (_pmu_value_to_trim(mid) << GC_XO_OSC_RC_TRIM_LSB)
|
||||
| (0x1 << GC_XO_OSC_RC_EN_LSB);
|
||||
|
||||
/* Set EN bit to lock in this trim_code */
|
||||
/* GWRITE_FIELD(XO, OSC_RC, EN, 1); */
|
||||
|
||||
/* 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 */
|
||||
/* pmu_clock_switch_rc_trim(); */
|
||||
|
||||
return _pmu_value_to_trim(mid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch system clock to RC no trim
|
||||
*/
|
||||
uint32_t pmu_clock_switch_rc_notrim(void)
|
||||
{
|
||||
uint32_t osc_sel;
|
||||
|
||||
/* check which clock we are running on */
|
||||
osc_sel = GR_PMU_OSC_SELECT_STAT;
|
||||
|
||||
if (osc_sel == GC_PMU_OSC_SELECT_RC) {
|
||||
/* Already on untrimmed RC */
|
||||
return 0;
|
||||
} else if (osc_sel == GC_PMU_OSC_SELECT_XTL) {
|
||||
/* Need to switch to RC trimmed first */
|
||||
pmu_clock_switch_rc_trim(1);
|
||||
}
|
||||
|
||||
/* Turn on XO clock */
|
||||
pmu_clock_en(PERIPH_XO);
|
||||
|
||||
/* Power up RC notrim clock if it's currently off */
|
||||
GWRITE_FIELD(PMU, CLRDIS, RC_NOTRIM, 1);
|
||||
|
||||
/* Switch to the clock */
|
||||
GR_PMU_OSC_HOLD_CLR = 0x1; /* make sure the hold signal is clear */
|
||||
GR_PMU_OSC_SELECT = GC_PMU_OSC_SELECT_RC;
|
||||
/* make sure the hold signal is set for future power downs */
|
||||
GR_PMU_OSC_HOLD_SET = 0x1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* enable clock doubler for USB purposes
|
||||
*/
|
||||
void pmu_enable_clock_doubler(void)
|
||||
{
|
||||
/* enable stuff */
|
||||
GREG32(XO, OSC_ADC_CAL_FREQ2X) =
|
||||
(GC_XO_OSC_ADC_CAL_FREQ2X_CNTL_DEFAULT
|
||||
<< GC_XO_OSC_ADC_CAL_FREQ2X_CNTL_LSB) |
|
||||
(1 << GC_XO_OSC_ADC_CAL_FREQ2X_EN_LSB);
|
||||
/* enable more stuff */
|
||||
GREG32(XO, OSC_CLKOUT) =
|
||||
(1 << GC_XO_OSC_CLKOUT_ADC_EN_LSB) |
|
||||
(1 << GC_XO_OSC_CLKOUT_PLL_EN_LSB) |
|
||||
(1 << GC_XO_OSC_CLKOUT_BADC_EN_LSB) |
|
||||
(1 << GC_XO_OSC_CLKOUT_USB_EN_LSB);
|
||||
|
||||
/* make sure doubled clock is selected */
|
||||
GREG32(XO, OSC_24_48B_SEL) = GC_XO_OSC_24_48B_SEL_DEFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch system clock to RC trim
|
||||
*/
|
||||
uint32_t pmu_clock_switch_rc_trim(uint32_t skip_calibration)
|
||||
{
|
||||
uint32_t trimmed;
|
||||
uint32_t trim_code;
|
||||
uint32_t osc_sel;
|
||||
|
||||
/* check which clock we are running on */
|
||||
osc_sel = GREG32(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
|
||||
*/
|
||||
GREG32(PMU, OSC_HOLD_SET) = 0x1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Turn on DXO clock so we can write in the trim code in */
|
||||
pmu_clock_en(PERIPH_XO);
|
||||
|
||||
/* Disable the RC Trim flops in the glitchless switch */
|
||||
GWRITE_FIELD(PMU, OSC_CTRL, RC_TRIM_READYB, 0x1);
|
||||
|
||||
/* Power up the clock if not already powered up */
|
||||
GREG32(PMU, CLRDIS) = 1 << GC_PMU_SETDIS_RC_TRIM_LSB;
|
||||
|
||||
/* Check for the trim code in the always-on domain
|
||||
* before looking at the fuse
|
||||
*/
|
||||
if (GREAD_FIELD(XO, OSC_RC_STATUS, EN)) {
|
||||
trim_code = GREAD_FIELD(XO, OSC_RC_STATUS, TRIM);
|
||||
trimmed = 1;
|
||||
} else if (GREAD_FIELD(PMU, FUSE_RD_RC_OSC_26MHZ, EN)) {
|
||||
trim_code = GREAD_FIELD(PMU, FUSE_RD_RC_OSC_26MHZ, TRIM);
|
||||
trimmed = 1;
|
||||
} else {
|
||||
if (skip_calibration) {
|
||||
trim_code = GREAD_FIELD(XO, OSC_RC, TRIM);
|
||||
trimmed = 0;
|
||||
} else {
|
||||
trim_code = pmu_calibrate_rc_trim();
|
||||
trimmed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the trim code to dxo */
|
||||
if (trimmed) {
|
||||
/* clear the hold signal */
|
||||
GREG32(XO, OSC_CLRHOLD) = GC_XO_OSC_CLRHOLD_RC_TRIM_MASK;
|
||||
|
||||
/* Write the trim code and enable the trim code */
|
||||
GREG32(XO, OSC_RC) = (trim_code << GC_XO_OSC_RC_TRIM_LSB) |
|
||||
(1 << GC_XO_OSC_RC_EN_LSB);
|
||||
|
||||
/* set the hold signal */
|
||||
GREG32(XO, OSC_SETHOLD) = 1 << GC_XO_OSC_SETHOLD_RC_TRIM_LSB;
|
||||
}
|
||||
|
||||
/* Enable the flops for RC TRIM in the glitchless switch */
|
||||
GWRITE_FIELD(PMU, OSC_CTRL, RC_TRIM_READYB, 0x0);
|
||||
|
||||
/*
|
||||
* Switch the select signal
|
||||
* make sure the hold signal is clear
|
||||
*/
|
||||
GREG32(PMU, OSC_HOLD_CLR) = 0x1;
|
||||
GREG32(PMU, OSC_SELECT) = GC_PMU_OSC_SELECT_RC_TRIM;
|
||||
|
||||
/* make sure the hold signal is set for future power downs */
|
||||
GREG32(PMU, OSC_HOLD_SET) = 0x1;
|
||||
|
||||
return !trimmed;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch system clock to XO
|
||||
* @returns The value of XO_OSC_XTL_FSM_STATUS. 0 = okay, 1 = error.
|
||||
*/
|
||||
uint32_t pmu_clock_switch_xo(void)
|
||||
{
|
||||
uint32_t osc_sel;
|
||||
uint32_t trim_code, final_trim, fsm_done, fsm_status;
|
||||
|
||||
/* check which clock we are running on */
|
||||
osc_sel = GREG32(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
|
||||
*/
|
||||
GREG32(PMU, OSC_HOLD_SET) = 0x1;
|
||||
return 0;
|
||||
} else if (osc_sel == GC_PMU_OSC_SELECT_RC) {
|
||||
/*
|
||||
* RC untrimmed clock. We must go through
|
||||
* the trimmed clock first to avoid glitching
|
||||
*/
|
||||
pmu_clock_switch_rc_trim(1);
|
||||
}
|
||||
|
||||
/* Turn on DXO clock so we can write in the trim code in */
|
||||
pmu_clock_en(PERIPH_XO);
|
||||
|
||||
/* Disable the XTL Clock */
|
||||
GWRITE_FIELD(PMU, OSC_CTRL, XTL_READYB, 0x1);
|
||||
|
||||
/* Power up the clock if not already powered up */
|
||||
GREG32(PMU, CLRDIS) = 1 << GC_PMU_CLRDIS_XTL_LSB;
|
||||
|
||||
/* Try to find the trim code */
|
||||
trim_code = 0;
|
||||
|
||||
/*
|
||||
* Check for the trim code in the always-on domain
|
||||
* before looking at the fuse
|
||||
*/
|
||||
if (GREAD_FIELD(XO, OSC_XTL_TRIM_STAT, EN)) {
|
||||
/* nothing to do */
|
||||
trim_code = GREAD_FIELD(XO, OSC_XTL_TRIM_STAT, CODE);
|
||||
|
||||
} else if (GREAD_FIELD(PMU, FUSE_RD_XTL_OSC_26MHZ, EN)) {
|
||||
|
||||
/* push the fuse trim code as the saved trim code */
|
||||
trim_code = GREAD_FIELD(PMU, FUSE_RD_XTL_OSC_26MHZ, TRIM);
|
||||
|
||||
/* make sure the hold signal is clear */
|
||||
GREG32(XO, OSC_CLRHOLD) = 1 << GC_XO_OSC_CLRHOLD_XTL_LSB;
|
||||
GREG32(XO, OSC_XTL_TRIM) =
|
||||
(trim_code << GC_XO_OSC_XTL_TRIM_CODE_LSB)
|
||||
| (0x1 << GC_XO_OSC_XTL_TRIM_EN_LSB);
|
||||
} else {
|
||||
/* Run the crystal FSM to calibrate the crystal trim */
|
||||
fsm_done = GREG32(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 {
|
||||
/* reset FSM */
|
||||
GREG32(XO, OSC_XTL_FSM_EN) = 0x0;
|
||||
GREG32(XO, OSC_XTL_FSM_EN) = GC_XO_OSC_XTL_FSM_EN_KEY;
|
||||
while (!(fsm_done & GC_XO_OSC_XTL_FSM_DONE_MASK))
|
||||
fsm_done = GREG32(XO, OSC_XTL_FSM);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the status and final trim value */
|
||||
/* max_trim = GREAD_FIELD(XO, OSC_XTL_FSM_CFG, TRIM_MAX); */
|
||||
final_trim = GREAD_FIELD(XO, OSC_XTL_FSM, TRIM);
|
||||
fsm_status = GREAD_FIELD(XO, OSC_XTL_FSM, STATUS);
|
||||
|
||||
/*
|
||||
* Save the trim for future powerups
|
||||
* make sure the hold signal is clear (may have already been cleared)
|
||||
*/
|
||||
GREG32(XO, OSC_CLRHOLD) = 1 << GC_XO_OSC_CLRHOLD_XTL_LSB;
|
||||
GREG32(XO, OSC_XTL_TRIM) = (final_trim << GC_XO_OSC_XTL_TRIM_CODE_LSB) |
|
||||
(1 << GC_XO_OSC_XTL_TRIM_EN_LSB);
|
||||
|
||||
/* make sure the hold signal is set for future power downs */
|
||||
GREG32(XO, OSC_SETHOLD) = 1 << GC_XO_OSC_SETHOLD_XTL_LSB;
|
||||
|
||||
/* Enable the flops for XTL in the glitchless switch */
|
||||
GWRITE_FIELD(PMU, OSC_CTRL, XTL_READYB, 0x0);
|
||||
|
||||
/*
|
||||
* Switch the select signal
|
||||
* make sure the hold signal is clear
|
||||
*/
|
||||
GREG32(PMU, OSC_HOLD_CLR) = 0x1;
|
||||
GREG32(PMU, OSC_SELECT) = GC_PMU_OSC_SELECT_XTL;
|
||||
|
||||
/* make sure the hold signal is set for future power downs */
|
||||
GREG32(PMU, OSC_HOLD_SET) = 0x1;
|
||||
|
||||
return !fsm_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enter sleep mode and handle exiting from sleep mode
|
||||
* @warning The CPU must be in RC no trim mode before calling this function
|
||||
*/
|
||||
void pmu_sleep(void)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
/* Enable PMU sleep interrupts */
|
||||
GREG32(PMU, ICTRL) = 1 << GC_PMU_ICTRL_SLEEP_LSB;
|
||||
|
||||
/* nvic_irq_en(GC_IRQNUM_PMU_PMUINT); */
|
||||
|
||||
/* Enable CPU SLEEPDEEP */
|
||||
val = GREG32(M3, SCR);
|
||||
GREG32(M3, SCR) = val | 0x4;
|
||||
|
||||
/* Enable WIC mode */
|
||||
GREG32(PMU, SETWIC) = 1 << GC_PMU_SETWIC_PROC0_LSB;
|
||||
|
||||
/* Disable power domains for entering sleep mode */
|
||||
GREG32(PMU, SETDIS) = (1 << GC_PMU_SETDIS_START_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDL_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDA_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDSRM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BGAP_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDXO_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDXOLP_LSB) |
|
||||
(1 << GC_PMU_SETDIS_XTL_LSB) |
|
||||
(1 << GC_PMU_SETDIS_RC_TRIM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_RC_NOTRIM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BATMON_LSB) |
|
||||
(1 << GC_PMU_SETDIS_FST_BRNOUT_PWR_LSB) |
|
||||
(1 << GC_PMU_SETDIS_FST_BRNOUT_LSB);
|
||||
|
||||
/* Wait for exit interrupt
|
||||
* @todo Add code to disable all non-PMU interrupts.
|
||||
*/
|
||||
__asm__("wfi");
|
||||
|
||||
/* Disable WIC mode */
|
||||
GREG32(PMU, CLRWIC) = 1 << GC_PMU_CLRWIC_PROC0_LSB;
|
||||
|
||||
/* Disable CPU SLEEPDEEP */
|
||||
val = GREG32(M3, SCR);
|
||||
GREG32(M3, SCR) = val & (~0x4);
|
||||
|
||||
/* Re-enable power domains */
|
||||
GREG32(PMU, CLRDIS) = (1 << GC_PMU_CLRDIS_START_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDL_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDA_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDSRM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDIOF_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BGAP_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDXO_LSB);
|
||||
|
||||
#ifdef __FIX_ME__
|
||||
GREG32(PMU, CLRDIS) = (1 << GC_PMU_CLRDIS_START_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDL_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDA_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDSRM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BGAP_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDXO_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDXOLP_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_XTL_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_RC_TRIM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_RC_NOTRIM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BATMON_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_FST_BRNOUT_PWR_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_FST_BRNOUT_LSB);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Enter hibernate mode
|
||||
* This function does not return. The powerdown exit event will
|
||||
* cause the CPU to begin executing the system / app bootloader.
|
||||
* @warning The CPU must be in RC no trim mode
|
||||
*/
|
||||
void pmu_hibernate(void)
|
||||
{
|
||||
/* Turn off power to everything except retention domains */
|
||||
GREG32(PMU, SETDIS) = (1 << GC_PMU_SETDIS_START_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDL_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDA_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDSRM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDIOF_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDLK_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDSK_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BIAS_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BGAP_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDXO_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDXOLP_LSB) |
|
||||
(1 << GC_PMU_SETDIS_XTL_LSB) |
|
||||
(1 << GC_PMU_SETDIS_RC_TRIM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_RC_NOTRIM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BATMON_LSB) |
|
||||
(1 << GC_PMU_SETDIS_FST_BRNOUT_PWR_LSB) |
|
||||
(1 << GC_PMU_SETDIS_FST_BRNOUT_LSB);
|
||||
|
||||
/* Wait for powerdown */
|
||||
for (;;)
|
||||
__asm__("wfi");
|
||||
}
|
||||
|
||||
/*
|
||||
* Exit hibernate mode
|
||||
* This function should be called after a powerdown exit event.
|
||||
* It handles turning the power domains back on.
|
||||
* Clocks will be left in RC no trim.
|
||||
*/
|
||||
void pmu_hibernate_exit(void)
|
||||
{
|
||||
/* Turn on power to everything */
|
||||
GREG32(PMU, CLRDIS) = (1 << GC_PMU_CLRDIS_START_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDL_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDA_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDSRM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDIOF_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDLK_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDSK_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BIAS_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BGAP_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDXO_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDXOLP_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_XTL_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_RC_TRIM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_RC_NOTRIM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BATMON_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_FST_BRNOUT_PWR_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_FST_BRNOUT_LSB);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enter powerdown mode
|
||||
* This function does not return. The powerdown exit event will
|
||||
* cause the CPU to begin executing the system / app bootloader.
|
||||
* @warning The CPU must be in RC no trim mode
|
||||
*/
|
||||
void pmu_powerdown(void)
|
||||
{
|
||||
/* Turn off power to everything */
|
||||
GREG32(PMU, SETDIS) = (1 << GC_PMU_SETDIS_START_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDL_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDA_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDSRM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDIOF_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDLK_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDSK_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDSRK_LSB) |
|
||||
(1 << GC_PMU_SETDIS_RETCOMPREF_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BIAS_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BGAP_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDXO_LSB) |
|
||||
(1 << GC_PMU_SETDIS_VDDXOLP_LSB) |
|
||||
(1 << GC_PMU_SETDIS_XTL_LSB) |
|
||||
(1 << GC_PMU_SETDIS_RC_TRIM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_RC_NOTRIM_LSB) |
|
||||
(1 << GC_PMU_SETDIS_BATMON_LSB) |
|
||||
(1 << GC_PMU_SETDIS_FST_BRNOUT_PWR_LSB) |
|
||||
(1 << GC_PMU_SETDIS_FST_BRNOUT_LSB);
|
||||
|
||||
/* Wait for powerdown */
|
||||
for (;;)
|
||||
__asm__("wfi");
|
||||
}
|
||||
|
||||
/*
|
||||
* Exit powerdown mode
|
||||
* This function should be called after a powerdown exit event.
|
||||
* It handles turning the power domains back on.
|
||||
* Clocks will be left in RC no trim.
|
||||
*/
|
||||
void pmu_powerdown_exit(void)
|
||||
{
|
||||
/* Turn on power to everything */
|
||||
GREG32(PMU, CLRDIS) = (1 << GC_PMU_CLRDIS_START_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDL_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDA_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDSRM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDIOF_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDLK_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDSK_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDSRK_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_RETCOMPREF_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BIAS_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BGAP_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDXO_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_VDDXOLP_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_XTL_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_RC_TRIM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_RC_NOTRIM_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_BATMON_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_FST_BRNOUT_PWR_LSB) |
|
||||
(1 << GC_PMU_CLRDIS_FST_BRNOUT_LSB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle PMU interrupt
|
||||
*/
|
||||
void pmu_interrupt(void)
|
||||
{
|
||||
/* TBD */
|
||||
}
|
||||
DECLARE_IRQ(GC_IRQNUM_PMU_PMUINT, pmu_interrupt, 1);
|
||||
113
chip/g/pmu.h
Normal file
113
chip/g/pmu.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef INC_PMU_H_
|
||||
#define INC_PMU_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "registers.h"
|
||||
|
||||
enum {
|
||||
PERIPH_AES = 0x0,
|
||||
PERIPH_AES0 = 0x0,
|
||||
PERIPH_AES1 = 0x1,
|
||||
|
||||
PERIPH_CAMO = 0x2,
|
||||
PERIPH_CAMO0 = 0x2,
|
||||
|
||||
PERIPH_FLASH = 0x3,
|
||||
PERIPH_FLASH0 = 0x3,
|
||||
|
||||
GLOBALSEC = 0x4,
|
||||
GLOBALSEC0 = 0x4,
|
||||
|
||||
PERIPH_GPIO = 0x5,
|
||||
PERIPH_GPIO0 = 0x5,
|
||||
PERIPH_GPIO1 = 0x6,
|
||||
|
||||
PERIPH_I2C = 0x7,
|
||||
PERIPH_I2C0 = 0x7,
|
||||
PERIPH_I2C1 = 0x8,
|
||||
|
||||
PERIPH_I2CS = 0x9,
|
||||
PERIPH_I2CS0 = 0x9,
|
||||
|
||||
PERIPH_MAU = 0xa,
|
||||
PERIPH_PAU = 0xb,
|
||||
PERIPH_PINMUX = 0xc,
|
||||
PERIPH_PMU = 0xd,
|
||||
|
||||
PERIPH_RBOX = 0xe,
|
||||
PERIPH_RBOX0 = 0xe,
|
||||
|
||||
PERIPH_RTC = 0xf,
|
||||
PERIPH_RTC0 = 0xf,
|
||||
|
||||
PERIPH_SHA = 0x10,
|
||||
PERIPH_SHA0 = 0x10,
|
||||
|
||||
PERIPH_SPI = 0x11,
|
||||
PERIPH_SPI0 = 0x11,
|
||||
|
||||
PERIPH_SPS = 0x12,
|
||||
PERIPH_SPS0 = 0x12,
|
||||
|
||||
PERIPH_SWDP = 0x13,
|
||||
PERIPH_SWDP0 = 0x13,
|
||||
|
||||
PERIPH_TEMP = 0x14,
|
||||
PERIPH_TEMP0 = 0x14,
|
||||
|
||||
PERIPH_TIMEHS = 0x15,
|
||||
PERIPH_TIMEHS0 = 0x15,
|
||||
PERIPH_TIMEHS1 = 0x16,
|
||||
|
||||
PERIPH_TIMELS = 0x17,
|
||||
PERIPH_TIMELS0 = 0x17,
|
||||
|
||||
PERIPH_TRNG = 0x18,
|
||||
PERIPH_TRNG0 = 0x18,
|
||||
|
||||
PERIPH_UART = 0x19,
|
||||
PERIPH_UART0 = 0x19,
|
||||
PERIPH_UART1 = 0x1a,
|
||||
PERIPH_UART2 = 0x1b,
|
||||
|
||||
PERIPH_USB = 0x1c,
|
||||
PERIPH_USB0 = 0x1c,
|
||||
PERIPH_USB0_USB_PHY = 0x1d,
|
||||
|
||||
PERIPH_WATCHDOG = 0x1e,
|
||||
PERIPH_WATCHDOG0 = 0x1e,
|
||||
|
||||
PERIPH_XO = 0x1f,
|
||||
PERIPH_XO0 = 0x1f,
|
||||
|
||||
PERIPH_PERI = 0x20,
|
||||
PERIPH_PERI0 = 0x20,
|
||||
PERIPH_PERI1 = 0x21,
|
||||
|
||||
PERIPH_PERI_MATRIX = 0x22,
|
||||
};
|
||||
|
||||
typedef void (*pmu_clock_func)(uint32_t periph);
|
||||
extern void pmu_clock_en(uint32_t periph);
|
||||
extern void pmu_clock_dis(uint32_t periph);
|
||||
extern void pmu_peripheral_rst(uint32_t periph);
|
||||
extern uint32_t pmu_calibrate_rc_trim(void);
|
||||
extern uint32_t pmu_clock_switch_rc_notrim(void);
|
||||
extern uint32_t pmu_clock_switch_rc_trim(uint32_t skip_calibration);
|
||||
extern uint32_t pmu_clock_switch_xo(void);
|
||||
extern void pmu_sleep(void);
|
||||
extern void pmu_hibernate(void);
|
||||
extern void pmu_hibernate_exit(void);
|
||||
extern void pmu_powerdown(void);
|
||||
extern void pmu_powerdown_exit(void);
|
||||
|
||||
/*
|
||||
* enable clock doubler for USB purposes
|
||||
*/
|
||||
void pmu_enable_clock_doubler(void);
|
||||
#endif /* INC_PMU_H_ */
|
||||
Reference in New Issue
Block a user