1
0
Fork 0
mirror of https://github.com/CTCaer/hekate.git synced 2024-11-13 22:06:46 +00:00
hekate/bdk/soc/clock.c

939 lines
25 KiB
C
Raw Normal View History

2018-05-01 06:15:48 +01:00
/*
2018-08-05 12:40:32 +01:00
* Copyright (c) 2018 naehrwert
2022-01-20 10:32:02 +00:00
* Copyright (c) 2018-2022 CTCaer
2018-08-05 12:40:32 +01:00
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2018-05-01 06:15:48 +01:00
#include <soc/clock.h>
#include <soc/hw_init.h>
#include <soc/pmc.h>
#include <soc/t210.h>
#include <storage/sdmmc.h>
#include <utils/util.h>
2018-05-01 06:15:48 +01:00
typedef struct _clock_osc_t
{
u32 freq;
u16 min;
u16 max;
} clock_osc_t;
static const clock_osc_t _clock_osc_cnt[] = {
{ 12000, 706, 757 },
{ 13000, 766, 820 },
{ 16800, 991, 1059 },
{ 19200, 1133, 1210 },
{ 26000, 1535, 1638 },
{ 38400, 2268, 2418 },
{ 48000, 2836, 3023 }
};
2018-11-10 12:11:42 +00:00
/* clock_t: reset, enable, source, index, clk_src, clk_div */
2018-08-05 12:40:32 +01:00
static const clock_t _clock_uart[] = {
2020-07-17 14:50:17 +01:00
{ CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_CLK_SOURCE_UARTA, CLK_L_UARTA, 0, 2 },
{ CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_CLK_SOURCE_UARTB, CLK_L_UARTB, 0, 2 },
{ CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_UARTC, CLK_H_UARTC, 0, 2 },
{ CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_CONTROLLER_CLK_SOURCE_UARTD, CLK_U_UARTD, 0, 2 },
{ CLK_RST_CONTROLLER_RST_DEVICES_Y, CLK_RST_CONTROLLER_CLK_OUT_ENB_Y, CLK_RST_CONTROLLER_CLK_SOURCE_UARTAPE, CLK_Y_UARTAPE, 0, 2 }
2018-05-01 06:15:48 +01:00
};
//I2C default parameters - TLOW: 4, THIGH: 2, DEBOUNCE: 0, FM_DIV: 26.
2018-05-01 06:15:48 +01:00
static const clock_t _clock_i2c[] = {
2020-07-17 14:50:17 +01:00
{ CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_CLK_SOURCE_I2C1, CLK_L_I2C1, 0, 19 }, //20.4MHz -> 100KHz
{ CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_I2C2, CLK_H_I2C2, 0, 4 }, //81.6MHz -> 400KHz
{ CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_CONTROLLER_CLK_SOURCE_I2C3, CLK_U_I2C3, 0, 4 }, //81.6MHz -> 400KHz
{ CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_CONTROLLER_CLK_SOURCE_I2C4, CLK_V_I2C4, 0, 19 }, //20.4MHz -> 100KHz
{ CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, CLK_H_I2C5, 0, 4 }, //81.6MHz -> 400KHz
{ CLK_RST_CONTROLLER_RST_DEVICES_X, CLK_RST_CONTROLLER_CLK_OUT_ENB_X, CLK_RST_CONTROLLER_CLK_SOURCE_I2C6, CLK_X_I2C6, 0, 19 } //20.4MHz -> 100KHz
2018-05-01 06:15:48 +01:00
};
2018-11-10 12:11:42 +00:00
static clock_t _clock_se = {
CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_CONTROLLER_CLK_SOURCE_SE, CLK_V_SE, 0, 0 // 408MHz.
2018-11-10 12:11:42 +00:00
};
2019-06-30 01:15:46 +01:00
static clock_t _clock_tzram = {
2020-07-17 14:50:17 +01:00
CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_NO_SOURCE, CLK_V_TZRAM, 0, 0
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_host1x = {
CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X, CLK_L_HOST1X, 4, 3 // 163.2MHz.
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_tsec = {
CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_CONTROLLER_CLK_SOURCE_TSEC, CLK_U_TSEC, 0, 2 // 204MHz.
2018-11-10 12:11:42 +00:00
};
2022-02-14 22:14:53 +00:00
static clock_t _clock_nvdec = {
CLK_RST_CONTROLLER_RST_DEVICES_Y, CLK_RST_CONTROLLER_CLK_OUT_ENB_Y, 0x698, CLK_Y_NVDEC, 4, 0 // 408 MHz.
};
static clock_t _clock_nvjpg = {
CLK_RST_CONTROLLER_RST_DEVICES_Y, CLK_RST_CONTROLLER_CLK_OUT_ENB_Y, 0x69C, CLK_Y_NVJPG, 4, 0 // 408 MHz.
};
2018-11-10 12:11:42 +00:00
static clock_t _clock_sor_safe = {
2020-07-17 14:50:17 +01:00
CLK_RST_CONTROLLER_RST_DEVICES_Y, CLK_RST_CONTROLLER_CLK_OUT_ENB_Y, CLK_NO_SOURCE, CLK_Y_SOR_SAFE, 0, 0
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_sor0 = {
CLK_RST_CONTROLLER_RST_DEVICES_X, CLK_RST_CONTROLLER_CLK_OUT_ENB_X, CLK_NOT_USED, CLK_X_SOR0, 0, 0
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_sor1 = {
CLK_RST_CONTROLLER_RST_DEVICES_X, CLK_RST_CONTROLLER_CLK_OUT_ENB_X, CLK_RST_CONTROLLER_CLK_SOURCE_SOR1, CLK_X_SOR1, 0, 2 // 204MHz.
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_kfuse = {
2020-07-17 14:50:17 +01:00
CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_NO_SOURCE, CLK_H_KFUSE, 0, 0
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_cl_dvfs = {
2020-07-17 14:50:17 +01:00
CLK_RST_CONTROLLER_RST_DEVICES_W, CLK_RST_CONTROLLER_CLK_OUT_ENB_W, CLK_NO_SOURCE, CLK_W_DVFS, 0, 0
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_coresight = {
CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_CONTROLLER_CLK_SOURCE_CSITE, CLK_U_CSITE, 0, 4 // 136MHz.
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_pwm = {
CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_CLK_SOURCE_PWM, CLK_L_PWM, 6, 4 // Fref: 6.4MHz. HOS: PLLP / 54 = 7.55MHz.
2018-11-10 12:11:42 +00:00
};
static clock_t _clock_sdmmc_legacy_tm = {
2020-07-17 14:50:17 +01:00
CLK_RST_CONTROLLER_RST_DEVICES_Y, CLK_RST_CONTROLLER_CLK_OUT_ENB_Y, CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, CLK_Y_SDMMC_LEGACY_TM, 4, 66
};
2022-01-20 10:32:57 +00:00
static clock_t _clock_apbdma = {
CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_NO_SOURCE, CLK_H_APBDMA, 0, 0
};
static clock_t _clock_ahbdma = {
CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_NO_SOURCE, CLK_H_AHBDMA, 0, 0
};
2022-01-20 10:32:02 +00:00
static clock_t _clock_actmon = {
CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON, CLK_V_ACTMON, 6, 0 // 19.2MHz.
};
static clock_t _clock_extperiph1 = {
CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH1, CLK_V_EXTPERIPH1, 0, 0
};
static clock_t _clock_extperiph2 = {
CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH2, CLK_V_EXTPERIPH2, 2, 202 // 4.0MHz
};
2022-01-20 10:32:02 +00:00
2018-05-01 06:15:48 +01:00
void clock_enable(const clock_t *clk)
{
2018-08-05 12:40:32 +01:00
// Put clock into reset.
2020-11-25 23:41:45 +00:00
CLOCK(clk->reset) = (CLOCK(clk->reset) & ~BIT(clk->index)) | BIT(clk->index);
2018-08-05 12:40:32 +01:00
// Disable.
2020-11-25 23:41:45 +00:00
CLOCK(clk->enable) &= ~BIT(clk->index);
2018-08-05 12:40:32 +01:00
// Configure clock source if required.
2018-05-01 06:15:48 +01:00
if (clk->source)
CLOCK(clk->source) = clk->clk_div | (clk->clk_src << 29);
2018-08-05 12:40:32 +01:00
// Enable.
2020-11-25 23:41:45 +00:00
CLOCK(clk->enable) = (CLOCK(clk->enable) & ~BIT(clk->index)) | BIT(clk->index);
usleep(2);
2018-08-05 12:40:32 +01:00
// Take clock off reset.
2020-11-25 23:41:45 +00:00
CLOCK(clk->reset) &= ~BIT(clk->index);
2018-05-01 06:15:48 +01:00
}
void clock_disable(const clock_t *clk)
{
2018-08-05 12:40:32 +01:00
// Put clock into reset.
2020-11-25 23:41:45 +00:00
CLOCK(clk->reset) = (CLOCK(clk->reset) & ~BIT(clk->index)) | BIT(clk->index);
2018-08-05 12:40:32 +01:00
// Disable.
2020-11-25 23:41:45 +00:00
CLOCK(clk->enable) &= ~BIT(clk->index);
2018-05-01 06:15:48 +01:00
}
void clock_enable_fuse(bool enable)
2018-05-01 06:15:48 +01:00
{
2020-11-25 23:41:45 +00:00
// Enable Fuse registers visibility.
2018-06-08 10:42:24 +01:00
CLOCK(CLK_RST_CONTROLLER_MISC_CLK_ENB) = (CLOCK(CLK_RST_CONTROLLER_MISC_CLK_ENB) & 0xEFFFFFFF) | ((enable & 1) << 28);
2018-05-01 06:15:48 +01:00
}
void clock_enable_uart(u32 idx)
{
clock_enable(&_clock_uart[idx]);
}
2020-04-30 01:34:05 +01:00
void clock_disable_uart(u32 idx)
{
clock_disable(&_clock_uart[idx]);
}
2020-11-25 23:41:45 +00:00
#define UART_SRC_CLK_DIV_EN BIT(24)
2020-04-30 01:34:05 +01:00
int clock_uart_use_src_div(u32 idx, u32 baud)
{
u32 clk_src_div = CLOCK(_clock_uart[idx].source) & 0xE0000000;
if (baud == 3000000)
CLOCK(_clock_uart[idx].source) = clk_src_div | UART_SRC_CLK_DIV_EN | 15;
else if (baud == 1000000)
2020-04-30 01:34:05 +01:00
CLOCK(_clock_uart[idx].source) = clk_src_div | UART_SRC_CLK_DIV_EN | 49;
else
{
CLOCK(_clock_uart[idx].source) = clk_src_div | 2;
return 1;
}
return 0;
}
2018-05-01 06:15:48 +01:00
void clock_enable_i2c(u32 idx)
{
clock_enable(&_clock_i2c[idx]);
}
void clock_disable_i2c(u32 idx)
{
clock_disable(&_clock_i2c[idx]);
}
2018-05-01 06:15:48 +01:00
void clock_enable_se()
{
clock_enable(&_clock_se);
// Lock clock to always enabled if T210B01.
if (hw_get_chip_id() == GP_HIDREV_MAJOR_T210B01)
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SE) |= 0x100;
2018-05-01 06:15:48 +01:00
}
2019-06-30 01:15:46 +01:00
void clock_enable_tzram()
{
2019-06-30 01:15:46 +01:00
clock_enable(&_clock_tzram);
}
2018-05-01 06:15:48 +01:00
void clock_enable_host1x()
{
clock_enable(&_clock_host1x);
}
void clock_disable_host1x()
{
clock_disable(&_clock_host1x);
}
void clock_enable_tsec()
{
clock_enable(&_clock_tsec);
}
void clock_disable_tsec()
{
clock_disable(&_clock_tsec);
}
2022-02-14 22:14:53 +00:00
void clock_enable_nvdec()
{
clock_enable(&_clock_nvdec);
}
void clock_disable_nvdec()
{
clock_disable(&_clock_nvdec);
}
void clock_enable_nvjpg()
{
clock_enable(&_clock_nvjpg);
}
void clock_disable_nvjpg()
{
clock_disable(&_clock_nvjpg);
}
2018-05-01 06:15:48 +01:00
void clock_enable_sor_safe()
{
clock_enable(&_clock_sor_safe);
}
void clock_disable_sor_safe()
{
clock_disable(&_clock_sor_safe);
}
void clock_enable_sor0()
{
clock_enable(&_clock_sor0);
}
void clock_disable_sor0()
{
clock_disable(&_clock_sor0);
}
void clock_enable_sor1()
{
clock_enable(&_clock_sor1);
}
void clock_disable_sor1()
{
clock_disable(&_clock_sor1);
}
void clock_enable_kfuse()
{
CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_SET) = BIT(CLK_H_KFUSE);
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_CLR) = BIT(CLK_H_KFUSE);
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) = BIT(CLK_H_KFUSE);
usleep(10); // Wait 10s to prevent glitching.
CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_CLR) = BIT(CLK_H_KFUSE);
usleep(20); // Wait 20s fo kfuse hw to init.
2018-05-01 06:15:48 +01:00
}
void clock_disable_kfuse()
{
clock_disable(&_clock_kfuse);
}
void clock_enable_cl_dvfs()
{
clock_enable(&_clock_cl_dvfs);
}
2018-08-05 12:40:32 +01:00
void clock_disable_cl_dvfs()
{
clock_disable(&_clock_cl_dvfs);
}
2018-05-01 06:15:48 +01:00
void clock_enable_coresight()
{
clock_enable(&_clock_coresight);
}
void clock_disable_coresight()
{
clock_disable(&_clock_coresight);
}
void clock_enable_pwm()
{
clock_enable(&_clock_pwm);
}
void clock_disable_pwm()
{
clock_disable(&_clock_pwm);
}
2022-01-20 10:32:57 +00:00
void clock_enable_apbdma()
{
clock_enable(&_clock_apbdma);
}
void clock_disable_apbdma()
{
clock_disable(&_clock_apbdma);
}
void clock_enable_ahbdma()
{
clock_enable(&_clock_ahbdma);
}
void clock_disable_ahbdma()
{
clock_disable(&_clock_ahbdma);
}
2022-01-20 10:32:02 +00:00
void clock_enable_actmon()
{
clock_enable(&_clock_actmon);
}
void clock_disable_actmon()
{
clock_disable(&_clock_actmon);
}
void clock_enable_extperiph1()
{
clock_enable(&_clock_extperiph1);
PMC(APBDEV_PMC_CLK_OUT_CNTRL) |= PMC_CLK_OUT_CNTRL_CLK1_SRC_SEL(OSC_CAR) | PMC_CLK_OUT_CNTRL_CLK1_FORCE_EN;
usleep(5);
}
void clock_disable_extperiph1()
{
PMC(APBDEV_PMC_CLK_OUT_CNTRL) &= ~((PMC_CLK_OUT_CNTRL_CLK1_SRC_SEL(OSC_CAR)) | PMC_CLK_OUT_CNTRL_CLK1_FORCE_EN);
clock_disable(&_clock_extperiph1);
}
void clock_enable_extperiph2()
{
clock_enable(&_clock_extperiph2);
PMC(APBDEV_PMC_CLK_OUT_CNTRL) |= PMC_CLK_OUT_CNTRL_CLK2_SRC_SEL(OSC_CAR) | PMC_CLK_OUT_CNTRL_CLK2_FORCE_EN;
usleep(5);
}
void clock_disable_extperiph2()
{
PMC(APBDEV_PMC_CLK_OUT_CNTRL) &= ~((PMC_CLK_OUT_CNTRL_CLK2_SRC_SEL(OSC_CAR)) | PMC_CLK_OUT_CNTRL_CLK2_FORCE_EN);
clock_disable(&_clock_extperiph2);
}
void clock_enable_plld(u32 divp, u32 divn, bool lowpower, bool tegra_t210)
{
u32 plld_div = (divp << 20) | (divn << 11) | 1;
// N divider is fractional, so N = DIVN + 1/2 + PLLD_SDM_DIN/8192.
u32 misc = 0x2D0000 | 0xFC00; // Clock enable and PLLD_SDM_DIN: -1024 -> DIVN + 0.375.
if (lowpower && tegra_t210)
misc = 0x2D0000 | 0x0AAA; // Clock enable and PLLD_SDM_DIN: 2730 -> DIVN + 0.833.
// Set DISP1 clock source and parent clock.
if (lowpower)
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_DISP1) = 0x40000000; // PLLD_OUT0.
// Set dividers and enable PLLD.
CLOCK(CLK_RST_CONTROLLER_PLLD_BASE) = PLLCX_BASE_ENABLE | PLLCX_BASE_LOCK | plld_div;
CLOCK(CLK_RST_CONTROLLER_PLLD_MISC1) = tegra_t210 ? 0x20 : 0; // Keep default PLLD_SETUP.
// Set PLLD_SDM_DIN and enable PLLD to DSI pads.
CLOCK(CLK_RST_CONTROLLER_PLLD_MISC) = misc;
}
void clock_enable_pllx()
{
// Configure and enable PLLX if disabled.
if (!(CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) & PLLX_BASE_ENABLE)) // PLLX_ENABLE.
{
CLOCK(CLK_RST_CONTROLLER_PLLX_MISC_3) &= ~PLLX_MISC3_IDDQ; // Disable IDDQ.
usleep(2);
// Set div configuration.
const u32 pllx_div_cfg = (2 << 20) | (156 << 8) | 2; // P div: 2 (3), N div: 156, M div: 2. 998.4 MHz.
// Bypass dividers.
CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) = PLLX_BASE_BYPASS | pllx_div_cfg;
// Disable bypass
CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) = pllx_div_cfg;
// Set PLLX_LOCK_ENABLE.
CLOCK(CLK_RST_CONTROLLER_PLLX_MISC) |= PLLX_MISC_LOCK_EN;
// Enable PLLX.
CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) = PLLX_BASE_ENABLE | pllx_div_cfg;
}
// Wait for PLL to stabilize.
while (!(CLOCK(CLK_RST_CONTROLLER_PLLX_BASE) & PLLX_BASE_LOCK))
;
}
void clock_enable_pllc(u32 divn)
{
u8 pll_divn_curr = (CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) >> 10) & 0xFF;
// Check if already enabled and configured.
if ((CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) & PLLCX_BASE_ENABLE) && (pll_divn_curr == divn))
return;
// Take PLLC out of reset and set basic misc parameters.
CLOCK(CLK_RST_CONTROLLER_PLLC_MISC) =
((CLOCK(CLK_RST_CONTROLLER_PLLC_MISC) & 0xFFF0000F) & ~PLLC_MISC_RESET) | (0x80000 << 4); // PLLC_EXT_FRU.
CLOCK(CLK_RST_CONTROLLER_PLLC_MISC_2) |= 0xF0 << 8; // PLLC_FLL_LD_MEM.
// Disable PLL and IDDQ in case they are on.
2020-11-25 23:41:45 +00:00
CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) &= ~PLLCX_BASE_ENABLE;
CLOCK(CLK_RST_CONTROLLER_PLLC_MISC_1) &= ~PLLC_MISC1_IDDQ;
usleep(10);
// Set PLLC dividers.
CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) = (divn << 10) | 4; // DIVM: 4, DIVP: 1.
// Enable PLLC and wait for Phase and Frequency lock.
CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) |= PLLCX_BASE_ENABLE;
while (!(CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) & PLLCX_BASE_LOCK))
;
// Disable PLLC_OUT1, enable reset and set div to 1.5.
2020-11-25 23:41:45 +00:00
CLOCK(CLK_RST_CONTROLLER_PLLC_OUT) = BIT(8);
// Enable PLLC_OUT1 and bring it out of reset.
CLOCK(CLK_RST_CONTROLLER_PLLC_OUT) |= (PLLC_OUT1_CLKEN | PLLC_OUT1_RSTN_CLR);
msleep(1); // Wait a bit for PLL to stabilize.
}
void clock_disable_pllc()
{
// Disable PLLC and PLLC_OUT1.
2020-11-25 23:41:45 +00:00
CLOCK(CLK_RST_CONTROLLER_PLLC_OUT) &= ~(PLLC_OUT1_CLKEN | PLLC_OUT1_RSTN_CLR);
CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) &= ~PLLCX_BASE_ENABLE;
CLOCK(CLK_RST_CONTROLLER_PLLC_BASE) |= PLLCX_BASE_REF_DIS;
CLOCK(CLK_RST_CONTROLLER_PLLC_MISC_1) |= PLLC_MISC1_IDDQ;
2020-11-25 23:41:45 +00:00
CLOCK(CLK_RST_CONTROLLER_PLLC_MISC) |= PLLC_MISC_RESET;
usleep(10);
}
2020-11-25 23:41:45 +00:00
#define PLLC4_ENABLED BIT(31)
#define PLLC4_IN_USE (~PLLC4_ENABLED)
u32 pllc4_enabled = 0;
static void _clock_enable_pllc4(u32 mask)
{
pllc4_enabled |= mask;
if (pllc4_enabled & PLLC4_ENABLED)
return;
// Enable Phase and Frequency lock detection.
//CLOCK(CLK_RST_CONTROLLER_PLLC4_MISC) = PLLC4_MISC_EN_LCKDET;
// Disable PLL and IDDQ in case they are on.
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) &= ~PLLCX_BASE_ENABLE;
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) &= ~PLLC4_BASE_IDDQ;
usleep(10);
// Set PLLC4 dividers.
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) = (104 << 8) | 4; // DIVM: 4, DIVP: 1.
// Enable PLLC4 and wait for Phase and Frequency lock.
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) |= PLLCX_BASE_ENABLE;
while (!(CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) & PLLCX_BASE_LOCK))
;
msleep(1); // Wait a bit for PLL to stabilize.
pllc4_enabled |= PLLC4_ENABLED;
}
static void _clock_disable_pllc4(u32 mask)
{
pllc4_enabled &= ~mask;
2020-06-14 11:19:53 +01:00
// Check if currently in use or disabled.
if ((pllc4_enabled & PLLC4_IN_USE) || !(pllc4_enabled & PLLC4_ENABLED))
return;
// Disable PLLC4.
2020-06-14 11:19:53 +01:00
msleep(1); // Wait at least 1ms to prevent glitching.
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) &= ~PLLCX_BASE_ENABLE;
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) |= PLLC4_BASE_IDDQ;
2020-06-14 11:19:53 +01:00
usleep(10);
pllc4_enabled = 0;
}
void clock_enable_pllu()
{
// Configure PLLU.
2020-11-25 23:41:45 +00:00
CLOCK(CLK_RST_CONTROLLER_PLLU_MISC) |= BIT(29); // Disable reference clock.
u32 pllu_cfg = (CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) & 0xFFE00000) | BIT(24) | (1 << 16) | (0x19 << 8) | 2;
CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) = pllu_cfg;
2020-11-25 23:41:45 +00:00
CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) = pllu_cfg | PLLCX_BASE_ENABLE; // Enable.
// Wait for PLL to stabilize.
u32 timeout = (u32)TMR(TIMERUS_CNTR_1US) + 1300;
2020-11-25 23:41:45 +00:00
while (!(CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) & PLLCX_BASE_LOCK)) // PLL_LOCK.
if ((u32)TMR(TIMERUS_CNTR_1US) > timeout)
break;
usleep(10);
// Enable PLLU USB/HSIC/ICUSB/48M.
CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) |= 0x2E00000;
}
void clock_disable_pllu()
{
CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) &= ~0x2E00000; // Disable PLLU USB/HSIC/ICUSB/48M.
CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) &= ~0x40000000; // Disable PLLU.
CLOCK(CLK_RST_CONTROLLER_PLLU_MISC) &= ~0x20000000; // Enable reference clock.
}
void clock_enable_utmipll()
{
// Set UTMIPLL dividers and config based on OSC and enable it to 960 MHz.
CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG0) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG0) & 0xFF0000FF) | (25 << 16) | (1 << 8); // 38.4Mhz * (25 / 1) = 960 MHz.
CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) & 0xFF00003F) | (24 << 18); // Set delay count for 38.4Mhz osc crystal.
CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) & 0x7FFA000) | (1 << 15) | 375;
// Wait for UTMIPLL to stabilize.
u32 retries = 10; // Wait 20us
while (!(CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) & UTMIPLL_LOCK) && retries)
{
usleep(1);
retries--;
}
}
2018-05-01 06:15:48 +01:00
static int _clock_sdmmc_is_reset(u32 id)
{
switch (id)
{
case SDMMC_1:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & BIT(CLK_L_SDMMC1);
2018-05-01 06:15:48 +01:00
case SDMMC_2:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & BIT(CLK_L_SDMMC2);
2018-05-01 06:15:48 +01:00
case SDMMC_3:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_U) & BIT(CLK_U_SDMMC3);
2018-05-01 06:15:48 +01:00
case SDMMC_4:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_L) & BIT(CLK_L_SDMMC4);
2018-05-01 06:15:48 +01:00
}
return 0;
}
static void _clock_sdmmc_set_reset(u32 id)
{
switch (id)
{
case SDMMC_1:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_SDMMC1);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_2:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_SDMMC2);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_3:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_SET) = BIT(CLK_U_SDMMC3);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_4:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_SDMMC4);
break;
2018-05-01 06:15:48 +01:00
}
}
static void _clock_sdmmc_clear_reset(u32 id)
{
switch (id)
{
case SDMMC_1:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = BIT(CLK_L_SDMMC1);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_2:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = BIT(CLK_L_SDMMC2);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_3:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_U_CLR) = BIT(CLK_U_SDMMC3);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_4:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = BIT(CLK_L_SDMMC4);
break;
2018-05-01 06:15:48 +01:00
}
}
static int _clock_sdmmc_is_enabled(u32 id)
{
switch (id)
{
case SDMMC_1:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & BIT(CLK_L_SDMMC1);
2018-05-01 06:15:48 +01:00
case SDMMC_2:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & BIT(CLK_L_SDMMC2);
2018-05-01 06:15:48 +01:00
case SDMMC_3:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_U) & BIT(CLK_U_SDMMC3);
2018-05-01 06:15:48 +01:00
case SDMMC_4:
2020-07-17 14:50:17 +01:00
return CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & BIT(CLK_L_SDMMC4);
2018-05-01 06:15:48 +01:00
}
return 0;
}
2018-06-08 10:42:24 +01:00
static void _clock_sdmmc_set_enable(u32 id)
2018-05-01 06:15:48 +01:00
{
switch (id)
{
case SDMMC_1:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = BIT(CLK_L_SDMMC1);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_2:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = BIT(CLK_L_SDMMC2);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_3:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_SET) = BIT(CLK_U_SDMMC3);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_4:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = BIT(CLK_L_SDMMC4);
break;
2018-05-01 06:15:48 +01:00
}
}
2018-06-08 10:42:24 +01:00
static void _clock_sdmmc_clear_enable(u32 id)
2018-05-01 06:15:48 +01:00
{
switch (id)
{
case SDMMC_1:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_SDMMC1);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_2:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_SDMMC2);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_3:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_U_CLR) = BIT(CLK_U_SDMMC3);
break;
2018-05-01 06:15:48 +01:00
case SDMMC_4:
2020-07-17 14:50:17 +01:00
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_SDMMC4);
break;
2018-05-01 06:15:48 +01:00
}
}
static void _clock_sdmmc_config_legacy_tm()
{
clock_t *clk = &_clock_sdmmc_legacy_tm;
2020-11-25 23:41:45 +00:00
if (!(CLOCK(clk->enable) & BIT(clk->index)))
clock_enable(clk);
}
typedef struct _clock_sdmmc_t
{
u32 clock;
u32 real_clock;
} clock_sdmmc_t;
static clock_sdmmc_t _clock_sdmmc_table[4] = { 0 };
2018-05-01 06:15:48 +01:00
#define SDMMC_CLOCK_SRC_PLLP_OUT0 0x0
#define SDMMC_CLOCK_SRC_PLLC4_OUT2 0x3
#define SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ 0x1
static int _clock_sdmmc_config_clock_host(u32 *pclock, u32 id, u32 val)
2018-05-01 06:15:48 +01:00
{
u32 divisor = 0;
u32 source = SDMMC_CLOCK_SRC_PLLP_OUT0;
if (id > SDMMC_4)
return 0;
2018-05-01 06:15:48 +01:00
2019-09-09 14:56:37 +01:00
// Get IO clock divisor.
2018-05-01 06:15:48 +01:00
switch (val)
{
case 25000:
*pclock = 24728;
2019-09-09 14:56:37 +01:00
divisor = 31; // 16.5 div.
2018-05-01 06:15:48 +01:00
break;
case 26000:
*pclock = 25500;
2019-09-09 14:56:37 +01:00
divisor = 30; // 16 div.
2018-05-01 06:15:48 +01:00
break;
case 40800:
*pclock = 40800;
2019-09-09 14:56:37 +01:00
divisor = 18; // 10 div.
2018-05-01 06:15:48 +01:00
break;
case 50000:
*pclock = 48000;
2019-09-09 14:56:37 +01:00
divisor = 15; // 8.5 div.
2018-05-01 06:15:48 +01:00
break;
case 52000:
*pclock = 51000;
2019-09-09 14:56:37 +01:00
divisor = 14; // 8 div.
2018-05-01 06:15:48 +01:00
break;
case 100000:
source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
*pclock = 99840;
divisor = 2; // 2 div.
2018-05-01 06:15:48 +01:00
break;
case 164000:
*pclock = 163200;
2019-09-09 14:56:37 +01:00
divisor = 3; // 2.5 div.
2018-05-01 06:15:48 +01:00
break;
case 200000: // 240MHz evo+.
switch (id)
{
case SDMMC_1:
source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
break;
case SDMMC_2:
source = SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ;
break;
case SDMMC_3:
source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
break;
case SDMMC_4:
source = SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ;
break;
}
*pclock = 199680;
divisor = 0; // 1 div.
2018-05-01 06:15:48 +01:00
break;
default:
*pclock = 24728;
2019-09-09 14:56:37 +01:00
divisor = 31; // 16.5 div.
2018-05-01 06:15:48 +01:00
}
_clock_sdmmc_table[id].clock = val;
_clock_sdmmc_table[id].real_clock = *pclock;
// Enable PLLC4 if in use by any SDMMC.
if (source)
2020-11-25 23:41:45 +00:00
_clock_enable_pllc4(BIT(id));
// Set SDMMC legacy timeout clock.
_clock_sdmmc_config_legacy_tm();
2018-05-01 06:15:48 +01:00
2019-12-04 19:31:39 +00:00
// Set SDMMC clock.
2018-05-01 06:15:48 +01:00
switch (id)
{
case SDMMC_1:
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1) = (source << 29) | divisor;
2018-05-01 06:15:48 +01:00
break;
case SDMMC_2:
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2) = (source << 29) | divisor;
2018-05-01 06:15:48 +01:00
break;
case SDMMC_3:
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3) = (source << 29) | divisor;
2018-05-01 06:15:48 +01:00
break;
case SDMMC_4:
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4) = (source << 29) | divisor;
2018-05-01 06:15:48 +01:00
break;
}
return 1;
}
void clock_sdmmc_config_clock_source(u32 *pclock, u32 id, u32 val)
2018-05-01 06:15:48 +01:00
{
if (_clock_sdmmc_table[id].clock == val)
2018-05-01 06:15:48 +01:00
{
*pclock = _clock_sdmmc_table[id].real_clock;
2018-05-01 06:15:48 +01:00
}
else
{
int is_enabled = _clock_sdmmc_is_enabled(id);
if (is_enabled)
_clock_sdmmc_clear_enable(id);
_clock_sdmmc_config_clock_host(pclock, id, val);
2018-05-01 06:15:48 +01:00
if (is_enabled)
_clock_sdmmc_set_enable(id);
_clock_sdmmc_is_reset(id);
}
}
void clock_sdmmc_get_card_clock_div(u32 *pclock, u16 *pdivisor, u32 type)
2018-05-01 06:15:48 +01:00
{
2019-12-04 19:31:39 +00:00
// Get Card clock divisor.
2018-05-01 06:15:48 +01:00
switch (type)
{
case SDHCI_TIMING_MMC_ID: // Actual IO Freq: 380.59 KHz.
*pclock = 26000;
2018-05-01 06:15:48 +01:00
*pdivisor = 66;
break;
case SDHCI_TIMING_MMC_LS26:
*pclock = 26000;
2018-05-01 06:15:48 +01:00
*pdivisor = 1;
break;
case SDHCI_TIMING_MMC_HS52:
*pclock = 52000;
2018-05-01 06:15:48 +01:00
*pdivisor = 1;
break;
case SDHCI_TIMING_MMC_HS200:
case SDHCI_TIMING_MMC_HS400:
case SDHCI_TIMING_UHS_SDR104:
*pclock = 200000;
2018-05-01 06:15:48 +01:00
*pdivisor = 1;
break;
case SDHCI_TIMING_SD_ID: // Actual IO Freq: 380.43 KHz.
*pclock = 25000;
2018-05-01 06:15:48 +01:00
*pdivisor = 64;
break;
case SDHCI_TIMING_SD_DS12:
case SDHCI_TIMING_UHS_SDR12:
*pclock = 25000;
*pdivisor = 1;
break;
case SDHCI_TIMING_SD_HS25:
case SDHCI_TIMING_UHS_SDR25:
*pclock = 50000;
2018-05-01 06:15:48 +01:00
*pdivisor = 1;
break;
case SDHCI_TIMING_UHS_SDR50:
*pclock = 100000;
2018-05-01 06:15:48 +01:00
*pdivisor = 1;
break;
case SDHCI_TIMING_UHS_SDR82:
*pclock = 164000;
2018-05-01 06:15:48 +01:00
*pdivisor = 1;
break;
case SDHCI_TIMING_UHS_DDR50:
*pclock = 40800;
2018-05-01 06:15:48 +01:00
*pdivisor = 1;
break;
2020-07-17 16:00:32 +01:00
case SDHCI_TIMING_MMC_HS102: // Actual IO Freq: 99.84 MHz.
*pclock = 200000;
2018-05-01 06:15:48 +01:00
*pdivisor = 2;
break;
}
}
int clock_sdmmc_is_not_reset_and_enabled(u32 id)
{
return !_clock_sdmmc_is_reset(id) && _clock_sdmmc_is_enabled(id);
}
void clock_sdmmc_enable(u32 id, u32 val)
{
u32 clock = 0;
2018-05-01 06:15:48 +01:00
if (_clock_sdmmc_is_enabled(id))
_clock_sdmmc_clear_enable(id);
_clock_sdmmc_set_reset(id);
_clock_sdmmc_config_clock_host(&clock, id, val);
2018-05-01 06:15:48 +01:00
_clock_sdmmc_set_enable(id);
_clock_sdmmc_is_reset(id);
usleep((100000 + clock - 1) / clock);
2018-05-01 06:15:48 +01:00
_clock_sdmmc_clear_reset(id);
_clock_sdmmc_is_reset(id);
}
void clock_sdmmc_disable(u32 id)
{
_clock_sdmmc_set_reset(id);
_clock_sdmmc_clear_enable(id);
_clock_sdmmc_is_reset(id);
2020-11-25 23:41:45 +00:00
_clock_disable_pllc4(BIT(id));
2018-05-01 06:15:48 +01:00
}
u32 clock_get_osc_freq()
{
CLOCK(CLK_RST_CONTROLLER_OSC_FREQ_DET) = OSC_FREQ_DET_TRIG | (2 - 1); // 2 periods of 32.76KHz window.
while (CLOCK(CLK_RST_CONTROLLER_OSC_FREQ_DET_STATUS) & OSC_FREQ_DET_BUSY)
;
u32 cnt = (CLOCK(CLK_RST_CONTROLLER_OSC_FREQ_DET_STATUS) & OSC_FREQ_DET_CNT);
CLOCK(CLK_RST_CONTROLLER_OSC_FREQ_DET) = 0;
// Return frequency in KHz.
for (u32 i = 0; i < ARRAY_SIZE(_clock_osc_cnt); i++)
if (cnt >= _clock_osc_cnt[i].min && cnt <= _clock_osc_cnt[i].max)
return _clock_osc_cnt[i].freq;
return 0;
}
u32 clock_get_dev_freq(clock_pto_id_t id)
{
const u32 pto_win = 16;
const u32 pto_osc = 32768;
u32 val = ((id & PTO_SRC_SEL_MASK) << PTO_SRC_SEL_SHIFT) | PTO_DIV_SEL_DIV1 | PTO_CLK_ENABLE | (pto_win - 1);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val | PTO_CNT_RST;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val | PTO_CNT_EN;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep((1000000 * pto_win / pto_osc) + 12 + 2);
while (CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS) & PTO_CLK_CNT_BUSY)
;
u32 cnt = CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS) & PTO_CLK_CNT;
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = 0;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
u32 freq_khz = (u64)cnt * pto_osc / pto_win / 1000;
return freq_khz;
}