soc: ite: it8xxx2: support 96MHz PLL frequency and lcvco calibration

To enhance USB clock's accuracy, this change supports 96MHz PLL frequency
and LCVCO calibration.

Signed-off-by: Ren Chen <Ren.Chen@ite.com.tw>
This commit is contained in:
Ren Chen 2023-10-26 08:46:32 +08:00 committed by Carles Cufí
commit b90a507ba1
3 changed files with 111 additions and 16 deletions

View file

@ -1279,6 +1279,12 @@ enum chip_pll_mode {
#define IT8XXX2_ECPM_SCDCR2 ECREG(IT8XXX2_ECPM_BASE + 0x0e)
#define IT8XXX2_ECPM_SCDCR3 ECREG(IT8XXX2_ECPM_BASE + 0x0f)
#define IT8XXX2_ECPM_SCDCR4 ECREG(IT8XXX2_ECPM_BASE + 0x10)
#define IT8XXX2_ECPM_PFACC0R ECREG(IT8XXX2_ECPM_BASE + 0x20)
#define IT8XXX2_ECPM_PFACC1R ECREG(IT8XXX2_ECPM_BASE + 0x21)
#define IT8XXX2_ECPM_PFACC2R ECREG(IT8XXX2_ECPM_BASE + 0x40)
#define IT8XXX2_ECPM_LCOTF2 ECREG(IT8XXX2_ECPM_BASE + 0x54)
#define IT8XXX2_ECPM_LCOCR ECREG(IT8XXX2_ECPM_BASE + 0x55)
#define IT8XXX2_ECPM_LCOCR1 ECREG(IT8XXX2_ECPM_BASE + 0x57)
/*
* The count number of the counter for 25 ms register.

View file

@ -154,6 +154,15 @@ config SOC_IT8XXX2_JTAG_DEBUG_INTERFACE
- GPIOA6 -> TRST
Supported I/O voltage is 3.3V.
config SOC_IT8XXX2_LCVCO
bool "LCVCO calibration"
depends on SOC_IT8XXX2_INT_32K
help
The LCVCO is a highly precise clock controller used for
calibrating the frequency shift of the PLL. Enabling this
option allows for supported LCVCO calibration, improving
the accuracy of the USB clock.
choice
prompt "Clock source for PLL reference clock"

View file

@ -31,6 +31,21 @@ COND_CODE_1(DT_NODE_EXISTS(DT_INST(1, ite_it8xxx2_usbpd)), (2), (1))
*/
#define SOC_USBPD_ITE_ACTIVE_PORT_COUNT DT_NUM_INST_STATUS_OKAY(ite_it8xxx2_usbpd)
/* PLL Frequency Auto-Calibration Control 0 Register */
#define PLL_FREQ_AUTO_CAL_EN BIT(7)
#define LOCK_TUNING_FACTORS_OF_LCO BIT(3)
/* LC Oscillator Control Register */
#define LCO_Power_CTRL BIT(1)
/* LC Oscillator Control Register 1 */
#define LDO_Power_CTRL BIT(1)
/* LC Oscillator Tuning Factor 2 */
#define LCO_SC_FACTOR_MASK GENMASK(6, 4)
#define LCO_SC_FACTOR(n) FIELD_PREP(LCO_SC_FACTOR_MASK, n)
/* PLL Frequency Auto-Calibration Control 2 Register */
#define AUTO_CAL_ENABLE BIT(1)
#define PLL_FREQ_AUTO_CAL_START BIT(0)
#define AUTO_CAL_ENABLE_AND_START (AUTO_CAL_ENABLE | PLL_FREQ_AUTO_CAL_START)
uint32_t chip_get_pll_freq(void)
{
uint32_t pllfreq;
@ -83,7 +98,9 @@ void __soc_ram_code chip_pll_ctrl(enum chip_pll_mode mode)
#ifdef CONFIG_SOC_IT8XXX2_PLL_FLASH_48M
struct pll_config_t {
uint8_t pll_freq;
uint8_t div_mcu;
uint8_t div_fnd;
uint8_t div_usb;
uint8_t div_uart;
uint8_t div_smb;
uint8_t div_sspi;
@ -93,10 +110,18 @@ struct pll_config_t {
uint8_t div_usbpd;
};
static const struct pll_config_t pll_configuration[] = {
enum pll_frequency {
PLL_FREQ_48M = 0,
PLL_FREQ_96M,
PLL_FREQ_CNT
};
static const struct pll_config_t pll_configuration[PLL_FREQ_CNT] = {
/*
* PLL frequency setting = 4 (48MHz)
* MCU div = 0 (PLL / 1 = 48 mhz)
* FND div = 0 (PLL / 1 = 48 mhz)
* USB div = 0 (PLL / 1 = 48 mhz)
* UART div = 1 (PLL / 2 = 24 mhz)
* SMB div = 1 (PLL / 2 = 24 mhz)
* SSPI div = 1 (PLL / 2 = 24 mhz)
@ -105,19 +130,49 @@ static const struct pll_config_t pll_configuration[] = {
* PWM div = 0 (PLL / 1 = 48 mhz)
* USBPD div = 5 (PLL / 6 = 8 mhz)
*/
{.pll_freq = 4,
.div_fnd = 0,
.div_uart = 1,
.div_smb = 1,
.div_sspi = 1,
[PLL_FREQ_48M] = {.pll_freq = 4,
.div_mcu = 0,
.div_fnd = 0,
.div_usb = 0,
.div_uart = 1,
.div_smb = 1,
.div_sspi = 1,
#ifdef CONFIG_SOC_IT8XXX2_EC_BUS_24MHZ
.div_ec = 1,
.div_ec = 1,
#else
.div_ec = 6,
.div_ec = 6,
#endif
.div_jtag = 1,
.div_pwm = 0,
.div_usbpd = 5}
.div_jtag = 1,
.div_pwm = 0,
.div_usbpd = 5},
/*
* PLL frequency setting = 7 (96MHz)
* MCU div = 1 (PLL / 2 = 48 mhz)
* FND div = 1 (PLL / 2 = 48 mhz)
* USB div = 1 (PLL / 2 = 48 mhz)
* UART div = 3 (PLL / 4 = 24 mhz)
* SMB div = 3 (PLL / 4 = 24 mhz)
* SSPI div = 3 (PLL / 4 = 24 mhz)
* EC div = 6 (FND / 6 = 8 mhz)
* JTAG div = 3 (PLL / 4 = 24 mhz)
* PWM div = 1 (PLL / 2 = 48 mhz)
* USBPD div = 11 (PLL / 12 = 8 mhz)
*/
[PLL_FREQ_96M] = {.pll_freq = 7,
.div_mcu = 1,
.div_fnd = 1,
.div_usb = 1,
.div_uart = 3,
.div_smb = 3,
.div_sspi = 3,
#ifdef CONFIG_SOC_IT8XXX2_EC_BUS_24MHZ
.div_ec = 1,
#else
.div_ec = 6,
#endif
.div_jtag = 3,
.div_pwm = 1,
.div_usbpd = 11},
};
void __soc_ram_code chip_run_pll_sequence(const struct pll_config_t *pll)
@ -142,12 +197,12 @@ void __soc_ram_code chip_run_pll_sequence(const struct pll_config_t *pll)
chip_pll_ctrl(CHIP_PLL_SLEEP);
/* Chip sleep and wait timer wake it up */
__asm__ volatile ("wfi");
/* New FND clock frequency */
IT8XXX2_ECPM_SCDCR0 = pll->div_fnd << 4;
/* New FND and MCU clock frequency */
IT8XXX2_ECPM_SCDCR0 = (pll->div_fnd << 4) | pll->div_mcu;
/* Chip doze after wfi instruction */
chip_pll_ctrl(CHIP_PLL_DOZE);
/* UART */
IT8XXX2_ECPM_SCDCR1 = pll->div_uart;
/* USB and UART */
IT8XXX2_ECPM_SCDCR1 = (pll->div_usb << 4) | pll->div_uart;
/* SSPI and SMB */
IT8XXX2_ECPM_SCDCR2 = (pll->div_sspi << 4) | pll->div_smb;
/* USBPD and PWM */
@ -182,8 +237,27 @@ static int chip_change_pll(void)
if (IS_ENABLED(CONFIG_HAS_ITE_INTC)) {
ite_intc_save_and_disable_interrupts();
}
/* Disable auto calibration before setting PLL frequency */
if (IT8XXX2_ECPM_PFACC0R & PLL_FREQ_AUTO_CAL_EN) {
IT8XXX2_ECPM_PFACC0R &= ~PLL_FREQ_AUTO_CAL_EN;
}
/* configure PLL/CPU/flash clock */
chip_configure_pll(&pll_configuration[0]);
if (IS_ENABLED(CONFIG_SOC_IT8XXX2_LCVCO)) {
chip_configure_pll(&pll_configuration[PLL_FREQ_96M]);
/* Enable LCVCO calibration */
IT8XXX2_ECPM_PFACC1R = 0x01;
IT8XXX2_ECPM_PFACC0R |= (PLL_FREQ_AUTO_CAL_EN | LOCK_TUNING_FACTORS_OF_LCO);
IT8XXX2_ECPM_LCOCR |= LCO_Power_CTRL;
IT8XXX2_ECPM_LCOCR1 |= LDO_Power_CTRL;
IT8XXX2_ECPM_LCOTF2 &= ~LCO_SC_FACTOR_MASK;
IT8XXX2_ECPM_LCOTF2 |= LCO_SC_FACTOR(2);
IT8XXX2_ECPM_PFACC2R = AUTO_CAL_ENABLE_AND_START;
} else {
chip_configure_pll(&pll_configuration[PLL_FREQ_48M]);
}
if (IS_ENABLED(CONFIG_HAS_ITE_INTC)) {
ite_intc_restore_interrupts();
}
@ -253,6 +327,12 @@ void riscv_idle(enum chip_pll_mode mode, unsigned int key)
*/
} while (ite_intc_no_irq());
if (IS_ENABLED(CONFIG_SOC_IT8XXX2_LCVCO)) {
if (mode != CHIP_PLL_DOZE) {
IT8XXX2_ECPM_PFACC2R |= PLL_FREQ_AUTO_CAL_START;
}
}
#ifdef CONFIG_ESPI
/* CPU has been woken up, the interrupt is no longer needed */
espi_it8xxx2_enable_trans_irq(ESPI_IT8XXX2_SOC_DEV, false);