mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-29 18:11:05 +00:00
Cr50: Enable the USB SOF clock auto-calibration
The timer clock nominally requires no firmware settings. It is tuned in manufacturing to be centered around 24MHz. However, it will potentially migrate away from 24MHz based upon variations in temperature and voltage. The variation is approximately 0.1-0.5MHz, based upon functional simulations, and backed up with observations in the lab. This CL enables a hardware feature to dynamically tune the timer clock if the device has an active USB port, by monitoring the SOF (start of frame) USB packets that are sent by the USB host every milllsecond with 500ppm accuracy. BUG=chrome-os-partner:50800 BRANCH=none TEST=make buildall; run on Cr50 hardware Verified that deep sleep, USB suspend/resume, etc continue to work with this enabled. Not too surprising, since I've never encountered a problem without it. In addition, I monitored XO_CLK_TIMER_CURRENT to see that the timer adjustments are being made while connecting and disconnecting from USB, entering andleaving sleep and deep sleep, etc. They are. Change-Id: I328b6416bc40ef8718815c5e09cf91ec1c6646f0 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/342145 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
This commit is contained in:
committed by
chrome-bot
parent
bdbf0810d0
commit
3ef613fa44
@@ -65,7 +65,7 @@
|
||||
#define PCLK_FREQ (24 * 1000 * 1000)
|
||||
|
||||
/* Number of IRQ vectors on the NVIC */
|
||||
#define CONFIG_IRQ_COUNT (GC_INTERRUPTS_COUNT - 16)
|
||||
#define CONFIG_IRQ_COUNT (GC_INTERRUPTS_COUNT - 15)
|
||||
|
||||
#undef CONFIG_RW_MEM_OFF
|
||||
#undef CONFIG_RW_SIZE
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
#define __CROS_EC_INIT_CHIP_H
|
||||
|
||||
void init_jittery_clock(int highsec);
|
||||
void init_sof_clock(void);
|
||||
|
||||
#endif /* __CROS_EC_INIT_CHIP_H */
|
||||
|
||||
162
chip/g/jitter.c
162
chip/g/jitter.c
@@ -7,13 +7,16 @@
|
||||
#include "console.h"
|
||||
#include "init_chip.h"
|
||||
#include "registers.h"
|
||||
#include "task.h"
|
||||
|
||||
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
|
||||
|
||||
void init_jittery_clock(int highsec)
|
||||
{
|
||||
unsigned trimfast = GR_FUSE(RC_JTR_OSC60_CC_TRIM);
|
||||
unsigned trim48 = GR_FUSE(RC_JTR_OSC48_CC_TRIM);
|
||||
unsigned delta = (trim48 - trimfast);
|
||||
/* for metastability reasons, avoid clk_jtr ~= clk_timer, make
|
||||
/* For metastability reasons, avoid clk_jtr ~= clk_timer, make
|
||||
* a keepout region around 24MHz of about 0.75MHz, about 3/16 of the
|
||||
* the delta from trimfast and trim48 */
|
||||
unsigned skiplow = (trim48 << 4) - (delta * 6);
|
||||
@@ -49,3 +52,160 @@ void init_jittery_clock(int highsec)
|
||||
GREG32(XO, CFG_WR_EN) = 0;
|
||||
GREG32(XO, JTR_CTRL_EN) = 0;
|
||||
}
|
||||
|
||||
void init_sof_clock(void)
|
||||
{
|
||||
/* Copy fuse value into software registers, both coarse and fine */
|
||||
unsigned coarseTrimVal = GR_FUSE(RC_TIMER_OSC48_CC_TRIM);
|
||||
unsigned fineTrimVal = GR_FUSE(RC_TIMER_OSC48_FC_TRIM);
|
||||
|
||||
/* We think SOF toggle happens once every mS, or ~24000 clock ticks */
|
||||
unsigned targetCnt = PCLK_FREQ / 1000;
|
||||
|
||||
/* The possible operations of a particular calibration bucket */
|
||||
unsigned binaryDnOp = 0x1 | 0x1 << 4;
|
||||
unsigned binaryUpOp = 0x1 | 0x0 << 4;
|
||||
unsigned subOp = 0x3 | 0x1 << 4;
|
||||
unsigned addOp = 0x2 | 0x1 << 4;
|
||||
unsigned nop = 0;
|
||||
|
||||
GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM) = coarseTrimVal;
|
||||
GREG32(XO, CLK_TIMER_RC_FINE_ATE_TRIM) = fineTrimVal;
|
||||
|
||||
/* Coarse trim values come from software */
|
||||
GWRITE_FIELD(XO, CLK_TIMER_TRIM_CTRL, RC_COARSE_TRIM_SRC, 0);
|
||||
|
||||
/* enable error interrupts
|
||||
* This enables underrun and overflow interrupts */
|
||||
GREG32(XO, DXO_INT_ENABLE) = 0xC;
|
||||
|
||||
/* Setup SOF calibration buckets and associated operations */
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB0) = targetCnt * 70 / 100;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB1) = targetCnt * 80 / 100;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB2) = targetCnt * 90 / 100;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB3) =
|
||||
targetCnt * (1000000 - 1250) / 1000000;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB4) = targetCnt;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB5) =
|
||||
targetCnt * (1000000 + 1250) / 1000000;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB6) = targetCnt * 110 / 100;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB7) = targetCnt * 120 / 100;
|
||||
|
||||
/* This is a work-around for the screwy SOF */
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL0) = nop;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL1) = binaryDnOp;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL2) = binaryDnOp;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL3) = subOp;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL4) = nop;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL5) = nop;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL6) = addOp;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL7) = binaryUpOp;
|
||||
GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL8) = binaryUpOp;
|
||||
|
||||
/* Set the calibration mode */
|
||||
GWRITE_FIELD(XO, CLK_TIMER_CALIB_TRIM_CTRL, ENABLE_FAST, 0);
|
||||
GWRITE_FIELD(XO, CLK_TIMER_CALIB_TRIM_CTRL, ENABLE_SLOW, 1);
|
||||
GWRITE_FIELD(XO, CLK_TIMER_CALIB_TRIM_CTRL, SLOW_MODE_SEL, 0); /* SOF */
|
||||
GWRITE_FIELD(XO, CLK_TIMER_CALIB_TRIM_CTRL, MAX_TRIM_SEL, 1);
|
||||
/* Don't stop when a NOP operation is seen, keep on calibrating */
|
||||
GWRITE_FIELD(XO, CLK_TIMER_CALIB_TRIM_CTRL, STOP_ON_NOP, 0);
|
||||
|
||||
/* Set source of trim codes:
|
||||
* coarse trim comes from software
|
||||
* fine trim comes from calibration engine */
|
||||
GWRITE_FIELD(XO, CLK_TIMER_TRIM_CTRL, RC_COARSE_TRIM_SRC, 0);
|
||||
GWRITE_FIELD(XO, CLK_TIMER_TRIM_CTRL, RC_FINE_TRIM_SRC, 1);
|
||||
|
||||
/* Enable dynamic trim */
|
||||
GWRITE_FIELD(XO, CLK_TIMER_TRIM_CTRL, RC_TRIM_EN, 1);
|
||||
|
||||
/* Sync everything! */
|
||||
GREG32(XO, CLK_TIMER_SYNC_CONTENTS) = 1;
|
||||
|
||||
/* Enable interrupts */
|
||||
task_enable_irq(GC_IRQNUM_XO0_SLOW_CALIB_UNDERRUN_INT);
|
||||
task_enable_irq(GC_IRQNUM_XO0_SLOW_CALIB_OVERFLOW_INT);
|
||||
}
|
||||
|
||||
/* When the calibration under runs, it means the fine trim code
|
||||
* has reached 0, but the clock is still too slow. Thus,
|
||||
* software must reduce the coarse trim code by 1 */
|
||||
static void timer_sof_calibration_underrun_int(void)
|
||||
{
|
||||
unsigned coarseTrimValue = GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM);
|
||||
|
||||
CPRINTS("%s: coarseTrimValue 0x%02x", __func__, coarseTrimValue);
|
||||
GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM) = coarseTrimValue + 1;
|
||||
|
||||
GREG32(XO, DXO_INT_STATE) =
|
||||
GC_XO_DXO_INT_STATE_SLOW_CALIB_UNDERRUN_MASK;
|
||||
}
|
||||
DECLARE_IRQ(GC_IRQNUM_XO0_SLOW_CALIB_UNDERRUN_INT,
|
||||
timer_sof_calibration_underrun_int, 1);
|
||||
|
||||
/* When the calibration overflows, it means the fine trim code
|
||||
* has reached 0x1F, but the clock is still too fast. Thus,
|
||||
* software must increase the coarse trim code by 1 */
|
||||
static void timer_sof_calibration_overflow_int(void)
|
||||
{
|
||||
unsigned coarseTrimValue = GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM);
|
||||
|
||||
CPRINTS("%s: coarseTrimValue 0x%02x", __func__, coarseTrimValue);
|
||||
GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM) = coarseTrimValue - 1;
|
||||
|
||||
GREG32(XO, DXO_INT_STATE) =
|
||||
GC_XO_DXO_INT_STATE_SLOW_CALIB_OVERFLOW_MASK;
|
||||
}
|
||||
DECLARE_IRQ(GC_IRQNUM_XO0_SLOW_CALIB_OVERFLOW_INT,
|
||||
timer_sof_calibration_overflow_int, 1);
|
||||
|
||||
#ifdef DEBUG_ME
|
||||
static int command_sof(int argc, char **argv)
|
||||
{
|
||||
ccprintf("FUSE_RC_TIMER_OSC48_CC_TRIM) 0x%08x\n",
|
||||
GR_FUSE(RC_TIMER_OSC48_CC_TRIM));
|
||||
ccprintf("FUSE_RC_TIMER_OSC48_FC_TRIM) 0x%08x\n",
|
||||
GR_FUSE(RC_TIMER_OSC48_FC_TRIM));
|
||||
|
||||
ccprintf("CLK_TIMER_RC_COARSE_ATE_TRIM 0x%08x\n",
|
||||
GREG32(XO, CLK_TIMER_RC_COARSE_ATE_TRIM));
|
||||
ccprintf("CLK_TIMER_RC_FINE_ATE_TRIM 0x%08x\n",
|
||||
GREG32(XO, CLK_TIMER_RC_FINE_ATE_TRIM));
|
||||
|
||||
ccprintf("CLK_TIMER_TRIM_CTRL 0x%08x\n",
|
||||
GREG32(XO, CLK_TIMER_TRIM_CTRL));
|
||||
|
||||
ccprintf("CLK_TIMER_CALIB_TRIM_CTRL 0x%08x\n",
|
||||
GREG32(XO, CLK_TIMER_CALIB_TRIM_CTRL));
|
||||
|
||||
ccprintf("DXO_INT_ENABLE 0x%08x\n",
|
||||
GREG32(XO, DXO_INT_ENABLE));
|
||||
|
||||
ccprintf("CLK_TIMER_SLOW_CALIB\n");
|
||||
ccprintf(" 0: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB0));
|
||||
ccprintf(" 1: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB1));
|
||||
ccprintf(" 2: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB2));
|
||||
ccprintf(" 3: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB3));
|
||||
ccprintf(" 4: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB4));
|
||||
ccprintf(" 5: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB5));
|
||||
ccprintf(" 6: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB6));
|
||||
ccprintf(" 7: 0x%04x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB7));
|
||||
|
||||
ccprintf("CLK_TIMER_SLOW_CALIB_CTRL\n");
|
||||
ccprintf(" 0: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL0));
|
||||
ccprintf(" 1: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL1));
|
||||
ccprintf(" 2: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL2));
|
||||
ccprintf(" 3: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL3));
|
||||
ccprintf(" 4: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL4));
|
||||
ccprintf(" 5: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL5));
|
||||
ccprintf(" 6: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL6));
|
||||
ccprintf(" 7: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL7));
|
||||
ccprintf(" 8: 0x%02x\n", GREG32(XO, CLK_TIMER_SLOW_CALIB_CTRL8));
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(sof, command_sof,
|
||||
"",
|
||||
"Display the SoF clock stuff",
|
||||
NULL);
|
||||
#endif /* DEBUG_ME */
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "console.h"
|
||||
#include "gpio.h"
|
||||
#include "hooks.h"
|
||||
#include "init_chip.h"
|
||||
#include "link_defs.h"
|
||||
#include "registers.h"
|
||||
#include "system.h"
|
||||
@@ -1073,6 +1074,9 @@ static void usb_reset(void)
|
||||
|
||||
/* Reinitialize all the endpoints */
|
||||
usb_init_endpoints();
|
||||
|
||||
/* Init the clock calibrator */
|
||||
init_sof_clock();
|
||||
}
|
||||
|
||||
static void usb_resetdet(void)
|
||||
@@ -1188,14 +1192,12 @@ static void usb_softreset(void)
|
||||
|
||||
void usb_connect(void)
|
||||
{
|
||||
CPRINTS("%s", __func__);
|
||||
print_later("usb_connect()", 0, 0, 0, 0, 0);
|
||||
GR_USB_DCTL &= ~DCTL_SFTDISCON;
|
||||
}
|
||||
|
||||
void usb_disconnect(void)
|
||||
{
|
||||
CPRINTS("%s", __func__);
|
||||
print_later("usb_disconnect()", 0, 0, 0, 0, 0);
|
||||
GR_USB_DCTL |= DCTL_SFTDISCON;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user