From 3ec66893e377b088c1284d2d23adb2aeea6d7965 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sat, 27 Feb 2021 22:08:58 +0000 Subject: New port: FiiO M3K on bare metal Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe --- firmware/target/mips/ingenic_x1000/clk-x1000.c | 258 +++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 firmware/target/mips/ingenic_x1000/clk-x1000.c (limited to 'firmware/target/mips/ingenic_x1000/clk-x1000.c') diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c new file mode 100644 index 0000000000..390d9722ac --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c @@ -0,0 +1,258 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "system.h" +#include "clk-x1000.h" +#include "x1000/cpm.h" +#include "x1000/msc.h" +#include "x1000/aic.h" + +static uint32_t pll_get(uint32_t pllreg, uint32_t onbit) +{ + if((pllreg & (1 << onbit)) == 0) + return 0; + + /* Both PLL registers share the same layout of N/M/OD bits. + * The max multiplier is 128 and max EXCLK is 26 MHz, so the + * multiplication should fit within 32 bits without overflow. + */ + uint32_t rate = X1000_EXCLK_FREQ; + rate *= jz_vreadf(pllreg, CPM_APCR, PLLM) + 1; + rate /= jz_vreadf(pllreg, CPM_APCR, PLLN) + 1; + rate >>= jz_vreadf(pllreg, CPM_APCR, PLLOD); + return rate; +} + +static uint32_t sclk_a_get(void) +{ + switch(jz_readf(CPM_CCR, SEL_SRC)) { + case 1: return X1000_EXCLK_FREQ; + case 2: return clk_get(X1000_CLK_APLL); + default: return 0; + } +} + +static uint32_t ccr_get(uint32_t selbit, uint32_t divbit) +{ + uint32_t reg = REG_CPM_CCR; + uint32_t sel = (reg >> selbit) & 0x3; + uint32_t div = (reg >> divbit) & 0xf; + + switch(sel) { + case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1); + case 2: return clk_get(X1000_CLK_MPLL) / (div + 1); + default: return 0; + } +} + +static uint32_t ddr_get(void) +{ + uint32_t reg = REG_CPM_DDRCDR; + uint32_t div = jz_vreadf(reg, CPM_DDRCDR, CLKDIV); + + switch(jz_vreadf(reg, CPM_DDRCDR, CLKSRC)) { + case 1: return clk_get(X1000_CLK_SCLK_A) / (div + 1); + case 2: return clk_get(X1000_CLK_MPLL) / (div + 1); + default: return 0; + } +} + +static uint32_t lcd_get(void) +{ + if(jz_readf(CPM_CLKGR, LCD)) + return 0; + + uint32_t reg = REG_CPM_LPCDR; + uint32_t rate; + switch(jz_vreadf(reg, CPM_LPCDR, CLKSRC)) { + case 0: rate = clk_get(X1000_CLK_SCLK_A); break; + case 1: rate = clk_get(X1000_CLK_MPLL); break; + default: return 0; + } + + rate /= jz_vreadf(reg, CPM_LPCDR, CLKDIV) + 1; + return rate; +} + +static uint32_t msc_get(int msc) +{ + if((msc == 0 && jz_readf(CPM_CLKGR, MSC0)) || + (msc == 1 && jz_readf(CPM_CLKGR, MSC1))) + return 0; + + uint32_t reg = REG_CPM_MSC0CDR; + uint32_t rate; + switch(jz_vreadf(reg, CPM_MSC0CDR, CLKSRC)) { + case 0: rate = clk_get(X1000_CLK_SCLK_A); break; + case 1: rate = clk_get(X1000_CLK_MPLL); break; + default: return 0; + } + + uint32_t div; + if(msc == 0) + div = jz_readf(CPM_MSC0CDR, CLKDIV); + else + div = jz_readf(CPM_MSC1CDR, CLKDIV); + + rate /= 2 * (div + 1); + rate >>= REG_MSC_CLKRT(msc); + return rate; +} + +static uint32_t i2s_mclk_get(void) +{ + if(jz_readf(CPM_CLKGR, AIC)) + return 0; + + uint32_t reg = REG_CPM_I2SCDR; + unsigned long long rate; + if(jz_vreadf(reg, CPM_I2SCDR, CS) == 0) + rate = X1000_EXCLK_FREQ; + else { + if(jz_vreadf(reg, CPM_I2SCDR, PCS) == 0) + rate = clk_get(X1000_CLK_SCLK_A); + else + rate = clk_get(X1000_CLK_MPLL); + + rate *= jz_vreadf(reg, CPM_I2SCDR, DIV_M); + rate /= jz_vreadf(reg, CPM_I2SCDR, DIV_N); + } + + /* Clamp invalid setting to 32 bits */ + if(rate > 0xffffffffull) + rate = 0xffffffff; + + return rate; +} + +static uint32_t i2s_bclk_get(void) +{ + return i2s_mclk_get() / (REG_AIC_I2SDIV + 1); +} + +static uint32_t sfc_get(void) +{ + if(jz_readf(CPM_CLKGR, SFC)) + return 0; + + uint32_t reg = REG_CPM_SSICDR; + uint32_t rate; + if(jz_vreadf(reg, CPM_SSICDR, SFC_CS) == 0) + rate = clk_get(X1000_CLK_SCLK_A); + else + rate = clk_get(X1000_CLK_MPLL); + + rate /= jz_vreadf(reg, CPM_SSICDR, CLKDIV) + 1; + return rate; +} + +uint32_t clk_get(x1000_clk_t clk) +{ + switch(clk) { + case X1000_CLK_EXCLK: return X1000_EXCLK_FREQ; + case X1000_CLK_APLL: return pll_get(REG_CPM_APCR, BP_CPM_APCR_ON); + case X1000_CLK_MPLL: return pll_get(REG_CPM_MPCR, BP_CPM_MPCR_ON); + case X1000_CLK_SCLK_A: return sclk_a_get(); + case X1000_CLK_CPU: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_CDIV); + case X1000_CLK_L2CACHE: return ccr_get(BP_CPM_CCR_SEL_CPLL, BP_CPM_CCR_L2DIV); + case X1000_CLK_AHB0: return ccr_get(BP_CPM_CCR_SEL_H0PLL, BP_CPM_CCR_H0DIV); + case X1000_CLK_AHB2: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_H2DIV); + case X1000_CLK_PCLK: return ccr_get(BP_CPM_CCR_SEL_H2PLL, BP_CPM_CCR_PDIV); + case X1000_CLK_DDR: return ddr_get(); + case X1000_CLK_LCD: return lcd_get(); + case X1000_CLK_MSC0: return msc_get(0); + case X1000_CLK_MSC1: return msc_get(1); + case X1000_CLK_I2S_MCLK: return i2s_mclk_get(); + case X1000_CLK_I2S_BCLK: return i2s_bclk_get(); + case X1000_CLK_SFC: return sfc_get(); + default: return 0; + } +} + +const char* clk_get_name(x1000_clk_t clk) +{ + switch(clk) { +#define CASE(x) case X1000_CLK_##x: return #x + CASE(EXCLK); + CASE(APLL); + CASE(MPLL); + CASE(SCLK_A); + CASE(CPU); + CASE(L2CACHE); + CASE(AHB0); + CASE(AHB2); + CASE(PCLK); + CASE(DDR); + CASE(LCD); + CASE(MSC0); + CASE(MSC1); + CASE(I2S_MCLK); + CASE(I2S_BCLK); + CASE(SFC); +#undef CASE + default: + return "NONE"; + } +} + +#define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL) +#define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX) +#define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY) + +void clk_set_ccr_mux(uint32_t muxbits) +{ + /* Set new mux configuration */ + uint32_t reg = REG_CPM_CCR; + reg &= ~CCR_MUX_BITS; + reg |= muxbits & CCR_MUX_BITS; + REG_CPM_CCR = reg; + + /* Wait for mux change to complete */ + while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS); +} + +void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk) +{ + /* Set new divider configuration */ + jz_writef(CPM_CCR, CDIV(cpu - 1), L2DIV(l2 - 1), + H0DIV(ahb0 - 1), H2DIV(ahb2 - 1), PDIV(pclk - 1), + CE_CPU(1), CE_AHB0(1), CE_AHB2(1)); + + /* Wait until divider change completes */ + while(REG_CPM_CSR & CSR_DIV_BITS); + + /* Disable CE bits after change */ + jz_writef(CPM_CCR, CE_CPU(0), CE_AHB0(0), CE_AHB2(0)); +} + +void clk_set_ddr(x1000_clk_t src, uint32_t div) +{ + /* Write new configuration */ + jz_writef(CPM_DDRCDR, CE(1), CLKDIV(div - 1), + CLKSRC(src == X1000_CLK_MPLL ? 2 : 1)); + + /* Wait until mux and divider change are complete */ + while(jz_readf(CPM_CSR, DDR_MUX) == 0); + while(jz_readf(CPM_DDRCDR, BUSY)); + + /* Disable CE bit after change */ + jz_writef(CPM_DDRCDR, CE(0)); +} -- cgit v1.2.3