diff options
author | Tzung-Bi Shih <tzungbi@chromium.org> | 2020-05-13 17:44:39 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2020-09-29 04:55:56 +0000 |
commit | b16c033100f11fb6f290d1fb8f47fbffac369e1e (patch) | |
tree | 4a3f15af212a9fd0f18ac97b9fae4f5c865be4a3 | |
parent | 485c236b2c0d455c673d1d6677055a3e801b41bb (diff) | |
download | chrome-ec-b16c033100f11fb6f290d1fb8f47fbffac369e1e.tar.gz |
chip/mt8192_scp: support ULPOSC
Supports ULPOSC and selects ULPOSC2 (360MHz).
BRANCH=none
BUG=b:146213943
BUG=b:156222893
TEST=make BOARD=asurada_scp
Signed-off-by: Tzung-Bi Shih <tzungbi@chromium.org>
Signed-off-by: Weiyi Lu <weiyi.lu@mediatek.com>
Change-Id: Ib9f043bb5575a6e18fc64479c7b241e11e012b5c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2198823
Reviewed-by: Eric Yilun Lin <yllin@chromium.org>
-rw-r--r-- | chip/mt8192_scp/clock.c | 331 | ||||
-rw-r--r-- | chip/mt8192_scp/clock_chip.h | 23 | ||||
-rw-r--r-- | chip/mt8192_scp/registers.h | 118 | ||||
-rw-r--r-- | chip/mt8192_scp/system.c | 6 |
4 files changed, 478 insertions, 0 deletions
diff --git a/chip/mt8192_scp/clock.c b/chip/mt8192_scp/clock.c index c707e1340e..ee7970a71a 100644 --- a/chip/mt8192_scp/clock.c +++ b/chip/mt8192_scp/clock.c @@ -5,8 +5,339 @@ /* Clocks, PLL and power settings */ +#include <assert.h> +#include <string.h> + +#include "clock_chip.h" #include "clock.h" +#include "console.h" +#include "csr.h" +#include "registers.h" +#include "timer.h" + +#define CPRINTF(format, args...) cprintf(CC_CLOCK, format, ##args) + +static struct opp_ulposc_cfg { + uint32_t osc; + uint32_t div; + uint32_t fband; + uint32_t mod; + uint32_t cali; + uint32_t target_mhz; +} opp[] = { + { + .osc = 1, .target_mhz = 196, .div = 20, .fband = 10, .mod = 3, + .cali = 64, + }, + { + .osc = 0, .target_mhz = 260, .div = 14, .fband = 2, .mod = 0, + .cali = 64, + }, + { + .osc = 1, .target_mhz = 280, .div = 20, .fband = 2, .mod = 0, + .cali = 64, + }, + { + .osc = 1, .target_mhz = 360, .div = 20, .fband = 10, .mod = 0, + .cali = 64, + }, +}; + +static inline void clock_busy_udelay(int usec) +{ + /* + * Delaying by busy-looping, for place that can't use udelay because of + * the clock not configured yet. The value 28 is chosen approximately + * from experiment. + * + * `volatile' in order to avoid compiler to optimize the function out + * (otherwise, the function will be eliminated). + */ + volatile int i = usec * 28; + + while (--i) + ; +} + +static void clock_ulposc_config_default(struct opp_ulposc_cfg *opp) +{ + unsigned int val = 0; + + /* set div */ + val |= opp->div << OSC_DIV_SHIFT; + /* set F-band; I-band = 82 */ + val |= (opp->fband << OSC_FBAND_SHIFT) | (82 << OSC_IBAND_SHIFT); + /* set calibration */ + val |= opp->cali; + /* set control register 0 */ + AP_ULPOSC_CON0(opp->osc) = val; + + /* set mod */ + val = opp->mod << OSC_MOD_SHIFT; + /* rsv2 = 0, rsv1 = 41, cali_32k = 0 */ + val |= 41 << OSC_RSV1_SHIFT; + /* set control register 1 */ + AP_ULPOSC_CON1(opp->osc) = val; + + /* bias = 64 */ + AP_ULPOSC_CON2(opp->osc) = 64; +} + +static void clock_ulposc_config_cali(struct opp_ulposc_cfg *opp, + uint32_t cali_val) +{ + uint32_t val; + + val = AP_ULPOSC_CON0(opp->osc); + val &= ~OSC_CALI_MASK; + val |= cali_val; + AP_ULPOSC_CON0(opp->osc) = val; + + clock_busy_udelay(50); +} + +static uint32_t clock_ulposc_measure_freq(uint32_t osc) +{ + uint32_t result = 0; + int cnt; + + /* before select meter clock input, bit[1:0] = b00 */ + AP_CLK_DBG_CFG = (AP_CLK_DBG_CFG & ~DBG_MODE_MASK) | + DBG_MODE_SET_CLOCK; + + /* select source, bit[21:16] = clk_src */ + AP_CLK_DBG_CFG = (AP_CLK_DBG_CFG & ~DBG_BIST_SOURCE_MASK) | + (osc == 0 ? DBG_BIST_SOURCE_ULPOSC1 : + DBG_BIST_SOURCE_ULPOSC2); + + /* set meter divisor to 1, bit[31:24] = b00000000 */ + AP_CLK_MISC_CFG_0 = (AP_CLK_MISC_CFG_0 & ~MISC_METER_DIVISOR_MASK) | + MISC_METER_DIV_1; + + /* enable frequency meter, without start */ + AP_SCP_CFG_0 |= CFG_FREQ_METER_ENABLE; + + /* trigger frequency meter start */ + AP_SCP_CFG_0 |= CFG_FREQ_METER_RUN; + + /* + * Frequency meter counts cycles in 1 / (26 * 1024) second period. + * freq_in_hz = freq_counter * 26 * 1024 + * + * The hardware takes 38us to count cycles. Delay up to 100us, + * as clock_busy_udelay may not be accurate when sysclk is not 26Mhz + * (e.g. when recalibrating/measuring after boot). + */ + for (cnt = 100; cnt > 0; --cnt) { + clock_busy_udelay(1); + if (!(AP_SCP_CFG_0 & CFG_FREQ_METER_RUN)) { + result = CFG_FREQ_COUNTER(AP_SCP_CFG_1); + break; + } + } + + /* disable freq meter */ + AP_SCP_CFG_0 &= ~CFG_FREQ_METER_ENABLE; + + return result; +} + +#define CAL_MIS_RATE 40 +static int clock_ulposc_is_calibrated(struct opp_ulposc_cfg *opp) +{ + uint32_t curr, target; + + curr = clock_ulposc_measure_freq(opp->osc); + target = opp->target_mhz * 1024 / 26; + + /* check if calibrated value is in the range of target value +- 4% */ + if (curr > (target * (1000 - CAL_MIS_RATE) / 1000) && + curr < (target * (1000 + CAL_MIS_RATE) / 1000)) + return 1; + else + return 0; +} + +static uint32_t clock_ulposc_process_cali(struct opp_ulposc_cfg *opp) +{ + uint32_t current_val = 0; + uint32_t target_val = opp->target_mhz * 1024 / 26; + uint32_t middle, min = 0, max = OSC_CALI_MASK; + uint32_t diff_by_min, diff_by_max, cal_result; + + do { + middle = (min + max) / 2; + if (middle == min) + break; + + clock_ulposc_config_cali(opp, middle); + current_val = clock_ulposc_measure_freq(opp->osc); + + if (current_val > target_val) + max = middle; + else + min = middle; + } while (min <= max); + + clock_ulposc_config_cali(opp, min); + current_val = clock_ulposc_measure_freq(opp->osc); + if (current_val > target_val) + diff_by_min = current_val - target_val; + else + diff_by_min = target_val - current_val; + + clock_ulposc_config_cali(opp, max); + current_val = clock_ulposc_measure_freq(opp->osc); + if (current_val > target_val) + diff_by_max = current_val - target_val; + else + diff_by_max = target_val - current_val; + + if (diff_by_min < diff_by_max) + cal_result = min; + else + cal_result = max; + + clock_ulposc_config_cali(opp, cal_result); + if (!clock_ulposc_is_calibrated(opp)) + assert(0); + + return cal_result; +} + +static void clock_high_enable(int osc) +{ + /* enable high speed clock */ + SCP_CLK_ENABLE |= CLK_HIGH_EN; + + switch (osc) { + case 0: + /* after 150us, enable ULPOSC */ + clock_busy_udelay(150); + SCP_CLK_ENABLE |= CLK_HIGH_CG; + break; + case 1: + /* turn off ULPOSC2 high-core-disable switch */ + SCP_CLK_ON_CTRL &= ~HIGH_CORE_DIS_SUB; + /* after 150us, turn on ULPOSC2 high core clock gate */ + clock_busy_udelay(150); + SCP_CLK_HIGH_CORE_CG |= HIGH_CORE_CG; + clock_busy_udelay(50); + break; + default: + break; + } +} + +static void clock_high_disable(int osc) +{ + switch (osc) { + case 0: + SCP_CLK_ENABLE &= ~CLK_HIGH_CG; + clock_busy_udelay(50); + SCP_CLK_ENABLE &= ~CLK_HIGH_EN; + clock_busy_udelay(50); + break; + case 1: + SCP_CLK_HIGH_CORE_CG &= ~HIGH_CORE_CG; + clock_busy_udelay(50); + SCP_CLK_ON_CTRL |= HIGH_CORE_DIS_SUB; + clock_busy_udelay(50); + break; + default: + break; + } +} + +static void clock_calibrate_ulposc(struct opp_ulposc_cfg *opp) +{ + /* + * ULPOSC1(osc=0) is already + * - calibrated + * - enabled in coreboot + * - used by pmic wrapper + */ + if (opp->osc != 0) { + clock_high_disable(opp->osc); + clock_ulposc_config_default(opp); + clock_high_enable(opp->osc); + } + + /* Calibrate only if it is not accurate enough. */ + if (!clock_ulposc_is_calibrated(opp)) + opp->cali = clock_ulposc_process_cali(opp); + +#ifdef DEBUG + CPRINTF("osc:%u, target=%uMHz, cal:%u\n", + opp->osc, opp->target_mhz, opp->cali); +#endif +} + +void clock_select_clock(enum scp_clock_source src) +{ + /* + * DIV2 divider takes precedence over clock selection to prevent + * over-clocking. + */ + if (src == SCP_CLK_ULPOSC1) + SCP_CLK_DIV_SEL = CLK_DIV_SEL2; + + SCP_CLK_SW_SEL = src; + + if (src != SCP_CLK_ULPOSC1) + SCP_CLK_DIV_SEL = CLK_DIV_SEL1; +} void clock_init(void) { + int i; + + /* select default 26M system clock */ + clock_select_clock(SCP_CLK_26M); + + /* set VREQ to HW mode */ + SCP_CPU_VREQ_CTRL = VREQ_SEL | VREQ_DVFS_SEL; + SCP_CLK_CTRL_GENERAL_CTRL &= ~VREQ_PMIC_WRAP_SEL; + SCP_SEC_CTRL &= ~VREQ_SECURE_DIS; + + /* set DDREN to auto mode */ + SCP_SYS_CTRL |= AUTO_DDREN; + + /* set settle time */ + SCP_CLK_SYS_VAL = + (SCP_CLK_SYS_VAL & ~CLK_SYS_VAL_MASK) | CLK_SYS_VAL_VAL(1); + SCP_CLK_HIGH_VAL = + (SCP_CLK_HIGH_VAL & ~CLK_HIGH_VAL_MASK) | CLK_HIGH_VAL_VAL(1); + SCP_SLEEP_CTRL = + (SCP_SLEEP_CTRL & ~VREQ_COUNT_MASK) | VREQ_COUNT_VAL(1); + + /* turn off ULPOSC2 */ + SCP_CLK_ON_CTRL |= HIGH_CORE_DIS_SUB; + + /* calibrate ULPOSC */ + for (i = 0; i < ARRAY_SIZE(opp); ++i) + clock_calibrate_ulposc(&opp[i]); + + /* select ULPOSC2 high speed CPU clock */ + clock_select_clock(SCP_CLK_ULPOSC2); + + /* enable default clock gate */ + SCP_SET_CLK_CG |= CG_DMA_CH3 | CG_DMA_CH2 | CG_DMA_CH1 | CG_DMA_CH0 | + CG_I2C_MCLK | CG_MAD_MCLK | CG_AP2P_MCLK; +} + +#ifdef DEBUG +int command_ulposc(int argc, char *argv[]) +{ + int i; + + for (i = 0; i <= 1; ++i) + ccprintf("ULPOSC%u frequency: %u kHz\n", + i + 1, + clock_ulposc_measure_freq(i) * 26 * 1000 / 1024); + + return EC_SUCCESS; } +DECLARE_CONSOLE_COMMAND(ulposc, command_ulposc, "[ulposc]", + "Measure ULPOSC frequency"); +#endif diff --git a/chip/mt8192_scp/clock_chip.h b/chip/mt8192_scp/clock_chip.h new file mode 100644 index 0000000000..ee1c22be92 --- /dev/null +++ b/chip/mt8192_scp/clock_chip.h @@ -0,0 +1,23 @@ +/* Copyright 2020 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. + */ + +/* Clocks, PLL and power settings */ + +#ifndef __CROS_EC_CLOCK_CHIP_H +#define __CROS_EC_CLOCK_CHIP_H + +#include "registers.h" + +enum scp_clock_source { + SCP_CLK_26M = CLK_SW_SEL_26M, + SCP_CLK_32K = CLK_SW_SEL_32K, + SCP_CLK_ULPOSC2 = CLK_SW_SEL_ULPOSC2, + SCP_CLK_ULPOSC1 = CLK_SW_SEL_ULPOSC1, +}; + +/* Switches to use 'src' clock */ +void clock_select_clock(enum scp_clock_source src); + +#endif /* __CROS_EC_CLOCK_CHIP_H */ diff --git a/chip/mt8192_scp/registers.h b/chip/mt8192_scp/registers.h index b5c658f534..cde931a2ba 100644 --- a/chip/mt8192_scp/registers.h +++ b/chip/mt8192_scp/registers.h @@ -17,6 +17,35 @@ /* clock control */ #define SCP_CLK_CTRL_BASE (SCP_REG_BASE + 0x21000) +/* clock source select */ +#define SCP_CLK_SW_SEL REG32(SCP_CLK_CTRL_BASE + 0x0000) +#define CLK_SW_SEL_26M 0 +#define CLK_SW_SEL_32K 1 +#define CLK_SW_SEL_ULPOSC2 2 +#define CLK_SW_SEL_ULPOSC1 3 +#define SCP_CLK_ENABLE REG32(SCP_CLK_CTRL_BASE + 0x0004) +#define CLK_HIGH_EN BIT(1) /* ULPOSC */ +#define CLK_HIGH_CG BIT(2) +/* system clock counter value */ +#define SCP_CLK_SYS_VAL REG32(SCP_CLK_CTRL_BASE + 0x0014) +#define CLK_SYS_VAL_MASK (0x3ff << 0) +#define CLK_SYS_VAL_VAL(v) ((v) & CLK_SYS_VAL_MASK) +/* ULPOSC clock counter value */ +#define SCP_CLK_HIGH_VAL REG32(SCP_CLK_CTRL_BASE + 0x0018) +#define CLK_HIGH_VAL_MASK (0x1f << 0) +#define CLK_HIGH_VAL_VAL(v) ((v) & CLK_HIGH_VAL_MASK) +/* sleep mode control */ +#define SCP_SLEEP_CTRL REG32(SCP_CLK_CTRL_BASE + 0x0020) +#define SLP_CTRL_EN BIT(0) +#define VREQ_COUNT_MASK (0x7F << 1) +#define VREQ_COUNT_VAL(v) (((v) << 1) & VREQ_COUNT_MASK) +#define SPM_SLP_MODE BIT(8) +/* clock divider select */ +#define SCP_CLK_DIV_SEL REG32(SCP_CLK_CTRL_BASE + 0x0024) +#define CLK_DIV_SEL1 0 +#define CLK_DIV_SEL2 1 +#define CLK_DIV_SEL4 2 +#define CLK_DIV_SEL3 3 /* clock gate */ #define SCP_SET_CLK_CG REG32(SCP_CLK_CTRL_BASE + 0x0030) #define CG_TIMER_MCLK BIT(0) @@ -63,6 +92,33 @@ #define UART_CK_SW_STATUS_26M BIT(0) #define UART_CK_SW_STATUS_32K BIT(1) #define UART_CK_SW_STATUS_ULPOS BIT(2) +/* VREQ control */ +#define SCP_CPU_VREQ_CTRL REG32(SCP_CLK_CTRL_BASE + 0x0054) +#define VREQ_SEL BIT(0) +#define VREQ_VALUE BIT(4) +#define VREQ_EXT_SEL BIT(8) +#define VREQ_DVFS_SEL BIT(16) +#define VREQ_DVFS_VALUE BIT(20) +#define VREQ_DVFS_EXT_SEL BIT(24) +#define VREQ_SRCLKEN_SEL BIT(27) +#define VREQ_SRCLKEN_VALUE BIT(28) +/* clock on control */ +#define SCP_CLK_HIGH_CORE_CG REG32(SCP_CLK_CTRL_BASE + 0x005C) +#define HIGH_CORE_CG BIT(1) +#define SCP_CLK_ON_CTRL REG32(SCP_CLK_CTRL_BASE + 0x006C) +#define HIGH_AO BIT(0) +#define HIGH_DIS_SUB BIT(1) +#define HIGH_CG_AO BIT(2) +#define HIGH_CORE_AO BIT(4) +#define HIGH_CORE_DIS_SUB BIT(5) +#define HIGH_CORE_CG_AO BIT(6) +/* clock general control */ +#define SCP_CLK_CTRL_GENERAL_CTRL REG32(SCP_CLK_CTRL_BASE + 0x009C) +#define VREQ_PMIC_WRAP_SEL (0x2) + +/* system control */ +#define SCP_SYS_CTRL REG32(SCP_REG_BASE + 0x24000) +#define AUTO_DDREN BIT(9) /* IPC */ #define SCP_SCP2APMCU_IPC_SET REG32(SCP_REG_BASE + 0x24080) @@ -140,6 +196,9 @@ #define TIMER_IRQ_CLR BIT(5) #define SCP_IRQ_TIMER(n) CONCAT2(SCP_IRQ_TIMER, n) +/* secure control */ +#define SCP_SEC_CTRL REG32(SCP_REG_BASE + 0xA5000) +#define VREQ_SECURE_DIS BIT(4) /* memory remap */ #define SCP_R_REMAP_0X0123 REG32(SCP_REG_BASE + 0xA5060) #define SCP_R_REMAP_0X4567 REG32(SCP_REG_BASE + 0xA5064) @@ -148,12 +207,71 @@ /* external address: AP */ #define AP_REG_BASE 0x60000000 /* 0x10000000 remap to 0x6 */ +/* OSC meter */ +#define TOPCK_BASE AP_REG_BASE +#define AP_CLK_MISC_CFG_0 REG32(TOPCK_BASE + 0x0140) +#define MISC_METER_DIVISOR_MASK 0xff000000 +#define MISC_METER_DIV_1 0 +#define AP_CLK_DBG_CFG REG32(TOPCK_BASE + 0x017C) +#define DBG_MODE_MASK 3 +#define DBG_MODE_SET_CLOCK 0 +#define DBG_BIST_SOURCE_MASK (0x3f << 16) +#define DBG_BIST_SOURCE_ULPOSC1 (0x25 << 16) +#define DBG_BIST_SOURCE_ULPOSC2 (0x24 << 16) +#define AP_SCP_CFG_0 REG32(TOPCK_BASE + 0x0220) +#define CFG_FREQ_METER_RUN BIT(4) +#define CFG_FREQ_METER_ENABLE BIT(12) +#define AP_SCP_CFG_1 REG32(TOPCK_BASE + 0x0224) +#define CFG_FREQ_COUNTER(CFG1) ((CFG1) & 0xFFFF) /* AP GPIO */ #define AP_GPIO_BASE (AP_REG_BASE + 0x5000) #define AP_GPIO_MODE11_SET REG32(AP_GPIO_BASE + 0x03B4) #define AP_GPIO_MODE11_CLR REG32(AP_GPIO_BASE + 0x03B8) #define AP_GPIO_MODE20_SET REG32(AP_GPIO_BASE + 0x0444) #define AP_GPIO_MODE20_CLR REG32(AP_GPIO_BASE + 0x0448) +/* + * ULPOSC + * osc: 0 for ULPOSC1, 1 for ULPOSC2. + */ +#define AP_ULPOSC_CON0_BASE (AP_REG_BASE + 0xC2B0) +#define AP_ULPOSC_CON1_BASE (AP_REG_BASE + 0xC2B4) +#define AP_ULPOSC_CON2_BASE (AP_REG_BASE + 0xC2B8) +#define AP_ULPOSC_CON0(osc) \ + REG32(AP_ULPOSC_CON0_BASE + (osc) * 0x10) +#define AP_ULPOSC_CON1(osc) \ + REG32(AP_ULPOSC_CON1_BASE + (osc) * 0x10) +#define AP_ULPOSC_CON2(osc) \ + REG32(AP_ULPOSC_CON2_BASE + (osc) * 0x10) +/* + * AP_ULPOSC_CON0 + * bit0-6: calibration + * bit7-13: iband + * bit14-17: fband + * bit18-23: div + * bit24: cp_en + * bit25-31: reserved + */ +#define OSC_CALI_MASK 0x7f +#define OSC_IBAND_SHIFT 7 +#define OSC_FBAND_SHIFT 14 +#define OSC_DIV_SHIFT 18 +#define OSC_CP_EN BIT(24) +/* AP_ULPOSC_CON1 + * bit0-7: 32K calibration + * bit 8-15: rsv1 + * bit 16-23: rsv2 + * bit 24-25: mod + * bit26: div2_en + * bit27-31: reserved + */ +#define OSC_RSV1_SHIFT 8 +#define OSC_RSV2_SHIFT 16 +#define OSC_MOD_SHIFT 24 +#define OSC_DIV2_EN BIT(26) +/* AP_ULPOSC_CON2 + * bit0-7: bias + * bit8-31: reserved + */ /* IRQ numbers */ #define SCP_IRQ_GIPC_IN0 0 diff --git a/chip/mt8192_scp/system.c b/chip/mt8192_scp/system.c index 3c5b6c837b..8ee0cb382d 100644 --- a/chip/mt8192_scp/system.c +++ b/chip/mt8192_scp/system.c @@ -5,6 +5,7 @@ /* System : hardware specific implementation */ +#include "csr.h" #include "memmap.h" #include "registers.h" #include "system.h" @@ -12,6 +13,11 @@ void system_pre_init(void) { memmap_init(); + + /* enable CPU and platform low power CG */ + /* enable CPU DCM */ + set_csr(CSR_MCTREN, CSR_MCTREN_CG); + /* Disable jump (it has only RW) and enable MPU. */ /* TODO: implement MPU */ system_disable_jump(); |