3ec66893e3
Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe
258 lines
7.3 KiB
C
258 lines
7.3 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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));
|
|
}
|