MAX_CARLINK_A270S/MXC_A27-PCB4.5-270S/ArkmicroFiles/libcpu-amt630hv100/source/clock.c

432 lines
15 KiB
C
Raw Normal View History

2025-01-21 16:49:37 +08:00
#include "FreeRTOS.h"
#include "chip.h"
#include "board.h"
#define PLL_DIV_MASK 0xFF
#define PLL_DIV_OFFSET 0
#define PLL_NO_MASK 0x3
#define PLL_NO_OFFSET 12
#define PLL_ENA (1 << 14)
typedef struct {
uint32_t clkid;
uint32_t clktype;
uint32_t source;
int clksource[MAX_CLK_SOURCE_NUM];
int source_index;
int div;
uint32_t enable_reg[MAX_CLK_ENABLE_BITS];
int enable_offset[MAX_CLK_ENABLE_BITS];
int enable_bits;
union {
uint32_t fixed_freq;
struct {
int div;
int mult;
} fixed_fator_property;
struct {
uint32_t cfgreg;
uint32_t refclkreg;
uint32_t offset;
uint32_t mask;
} pll_property;
struct {
uint32_t cfgreg;
uint32_t analogreg;
} dds_property;
struct {
uint32_t cfgreg;
int index_offset;
int index_value;
uint32_t index_mask;
int div_offset;
int div_value;
uint32_t div_mask;
int div_mode;
uint32_t inv_reg;
int inv_offset;
uint32_t inv_mask;
int inv_value;
} sys_property;
} u;
} xClockProperty;
xClockProperty xClocks[] = {
{.clkid = CLK_XTAL32K, .clktype = FIXED_CLOCK, .u.fixed_freq = 32768},
{.clkid = CLK_XTAL24M, .clktype = FIXED_CLOCK, .u.fixed_freq = 24000000},
{.clkid = CLK_240MHZ, .clktype = FIXED_FACTOR_CLOCK, .clksource = {CLK_XTAL24M},
.u.fixed_fator_property.div = 1, .u.fixed_fator_property.mult = 10},
{.clkid = CLK_12MHZ, .clktype = FIXED_FACTOR_CLOCK, .clksource = {CLK_XTAL24M},
.u.fixed_fator_property.div = 2, .u.fixed_fator_property.mult = 1},
{.clkid = CLK_6MHZ, .clktype = FIXED_FACTOR_CLOCK, .clksource = {CLK_XTAL24M},
.u.fixed_fator_property.div = 4, .u.fixed_fator_property.mult = 1},
{.clkid = CLK_CPUPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x60000088, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 0, .u.pll_property.mask = 1},
{.clkid = CLK_SYSPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x6000008c, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 1, .u.pll_property.mask = 1},
{.clkid = CLK_DDRPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x60000090, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 2, .u.pll_property.mask = 1},
{.clkid = CLK_VPUPLL, .clktype = PLL_CLOCK, .clksource = {CLK_6MHZ, CLK_12MHZ},
.u.pll_property.cfgreg = 0x60000094, .u.pll_property.refclkreg = 0x60000140,
.u.pll_property.offset = 3, .u.pll_property.mask = 1},
{.clkid = CLK_DDR, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M,CLK_DDRPLL},
.u.sys_property.cfgreg = 0x60000040, .u.sys_property.index_offset = 24,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = -1,
.u.sys_property.div_offset = 26, .u.sys_property.div_mask = 0x7,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_CPU, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M,CLK_CPUPLL},
.u.sys_property.cfgreg = 0x60000040, .u.sys_property.index_offset = 0,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = -1,
.u.sys_property.div_offset = 2, .u.sys_property.div_mask = 0x7,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_AHB, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M,CLK_SYSPLL},
.u.sys_property.cfgreg = 0x60000040, .u.sys_property.index_offset = 8,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = -1,
.u.sys_property.div_offset = 10, .u.sys_property.div_mask = 0x7,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_APB, .clktype = SYS_CLOCK, .clksource = {CLK_AHB},
.u.sys_property.cfgreg = 0x60000040,
.u.sys_property.div_offset = 16, .u.sys_property.div_mask = 0x3,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_DOUBLE,},
{.clkid = CLK_SPI0, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_CPUPLL},
.enable_reg = {0x60000064, 0x60000064}, .enable_offset = {30, 31}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000064, .u.sys_property.index_offset = 4,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 0, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 4, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_SPI1, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_CPUPLL},
.u.sys_property.cfgreg = 0x60000064, .u.sys_property.index_offset = 20,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 16, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 10, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
#if DEVICE_TYPE_SELECT != EMMC_FLASH
{.clkid = CLK_SDMMC0, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_SYSPLL},
.enable_reg = {0x60000048, 0x60000050, 0x60000058}, .enable_offset = {0, 12, 15}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000048, .u.sys_property.index_offset = 7,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,},
#else
{.clkid = CLK_SDMMC0, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_SYSPLL},
.enable_reg = {0x60000048, 0x60000050, 0x60000058}, .enable_offset = {6, 12, 15}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000048, .u.sys_property.index_offset = 7,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 0, .u.sys_property.div_mask = 0x1f,
.u.sys_property.div_value = 10, .u.sys_property.div_mode = DIVMODE_PONEDOUBLE,},
#endif
{.clkid = CLK_LCD, .clktype = SYS_CLOCK, .clksource = {CLK_SYSPLL, CLK_VPUPLL, CLK_XTAL24M, CLK_XTAL24M},
.enable_reg = {0x60000050, 0x60000058, 0x60000058, 0x60000058}, .enable_offset = {3, 18, 17, 16}, .enable_bits = 4,
.u.sys_property.cfgreg = 0x6000004c, .u.sys_property.index_offset = 0,
.u.sys_property.index_mask = 0x3, .u.sys_property.index_value = 0,
.u.sys_property.div_offset = 3, .u.sys_property.div_mask = 0x1f,
.u.sys_property.div_value = 11, .u.sys_property.div_mode = DIVMODE_PLUSONE,
#ifdef LCD_CLK_INVERSE
.u.sys_property.inv_reg = 0x6000004c, .u.sys_property.inv_offset = 8,
.u.sys_property.inv_mask = 0x1, .u.sys_property.inv_value = 1,
#endif
},
{.clkid = CLK_TIMER, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_CPUPLL},
.u.sys_property.cfgreg = 0x60000068, .u.sys_property.index_offset = 4,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_offset = 0, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_MFC, .clktype = SYS_CLOCK, .clksource = {CLK_SYSPLL, CLK_VPUPLL, CLK_XTAL24M},
.enable_reg = {0x60000050, 0x60000058}, .enable_offset = {6, 23}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000068, .u.sys_property.index_offset = 8,
.u.sys_property.index_mask = 0x3, .u.sys_property.index_value = 1,
.u.sys_property.div_offset = 11, .u.sys_property.div_mask = 0x1f,
.u.sys_property.div_value = -1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_PWM, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M, CLK_240MHZ},
.enable_reg = {0x60000054, 0x60000058}, .enable_offset = {10, 10}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000044, .u.sys_property.index_offset = 8,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_offset = 4, .u.sys_property.div_mask = 0xf,
.u.sys_property.div_value = 1, .u.sys_property.div_mode = DIVMODE_PLUSONE,},
{.clkid = CLK_CAN0, .clktype = SYS_CLOCK, .clksource = {CLK_APB},
.enable_reg = {0x60000054}, .enable_offset = {19}, .enable_bits = 1,
.u.sys_property.div_value = 1,},
{.clkid = CLK_CAN1, .clktype = SYS_CLOCK, .clksource = {CLK_APB},
.enable_reg = {0x60000054}, .enable_offset = {20}, .enable_bits = 1,
.u.sys_property.div_value = 1,},
{.clkid = CLK_ADC, .clktype = SYS_CLOCK, .clksource = {CLK_XTAL24M},
.enable_reg = {0x60000054, 0x60000058}, .enable_offset = {14, 13}, .enable_bits = 2,
.u.sys_property.cfgreg = 0x60000068, .u.sys_property.div_offset = 16,
.u.sys_property.div_mask = 0x7fff, .u.sys_property.div_value = 642,
.u.sys_property.div_mode = DIVMODE_PONEDOUBLE,},
{.clkid = CLK_I2S, .clktype = SYS_CLOCK, .clksource = {CLK_240MHZ, CLK_SYSPLL},
.enable_reg = {0x60000054, 0x60000058, 0x60000058}, .enable_offset = {12, 12, 11}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000044, .u.sys_property.index_offset = 16,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_value = 1,},
{.clkid = CLK_I2S1, .clktype = SYS_CLOCK, .clksource = {CLK_240MHZ, CLK_SYSPLL},
.enable_reg = {0x60000054, 0x60000144, 0x60000144}, .enable_offset = {22, 1, 0}, .enable_bits = 3,
.u.sys_property.cfgreg = 0x60000140, .u.sys_property.index_offset = 4,
.u.sys_property.index_mask = 0x1, .u.sys_property.index_value = 0,
.u.sys_property.div_value = 1,},
};
#define CLOCK_NUM (sizeof(xClocks) / sizeof(xClocks[0]))
static xClockProperty *clk_get(uint32_t clkid)
{
int i;
for (i = 0; i < CLOCK_NUM; i++) {
if (xClocks[i].clkid == clkid) {
return &xClocks[i];
}
}
return NULL;
}
static uint32_t clk_fixed_get_rate(xClockProperty *clk)
{
return clk->u.fixed_freq;
}
static uint32_t clk_fixed_factor_get_rate(xClockProperty *clk)
{
xClockProperty *parentclk = clk_get(clk->clksource[0]);
configASSERT(parentclk);
return clk_fixed_get_rate(parentclk) * clk->u.fixed_fator_property.mult
/ clk->u.fixed_fator_property.div;
}
static uint32_t clk_pll_get_rate(xClockProperty *clk)
{
uint32_t parent_rate;
uint32_t div, no, reg;
configASSERT(clk);
parent_rate = ulClkGetRate(clk->clksource[clk->source_index]);
reg = readl(clk->u.pll_property.cfgreg);
no = (reg >> PLL_NO_OFFSET) & PLL_NO_MASK;
div = (reg >> PLL_DIV_OFFSET) & PLL_DIV_MASK;
return (parent_rate * div) / (1 << no);
}
static uint32_t clk_sys_get_rate(xClockProperty *clk)
{
uint32_t parent_rate;
configASSERT(clk);
configASSERT(clk->div);
parent_rate = ulClkGetRate(clk->clksource[clk->source_index]);
return parent_rate / clk->div;
}
static void clk_pll_init(xClockProperty *clk)
{
configASSERT(clk);
clk->source_index = (readl(clk->u.pll_property.refclkreg) >> clk->u.pll_property.offset)
& clk->u.pll_property.mask;
}
static int clk_get_div(int div, int divmode)
{
switch(divmode) {
case DIVMODE_NOZERO:
div = div ? div : 1;
break;
case DIVMODE_PLUSONE:
div = div + 1;
break;
case DIVMODE_DOUBLE:
div *= 2;
break;
case DIVMODE_EXPONENT:
div = 1 << div;
break;
case DIVMODE_PONEDOUBLE:
div = (div + 1) * 2;
break;
}
return div;
}
static __INLINE int clk_set_div(int div, int divmode)
{
switch(divmode) {
case DIVMODE_PLUSONE:
div = div - 1;
break;
case DIVMODE_DOUBLE:
div /= 2;
break;
case DIVMODE_EXPONENT:
div = fls(div) - 1;
break;
case DIVMODE_PONEDOUBLE:
div = div / 2 - 1;
break;
}
return div;
}
static void clk_sys_set_rate(xClockProperty *clk, uint32_t freq)
{
int div;
uint32_t reg;
uint32_t parent_rate;
configASSERT(clk);
parent_rate = ulClkGetRate(clk->clksource[clk->source_index]);
div = DIV_ROUND_UP(parent_rate, freq);
clk->div = div;
div = clk_set_div(div, clk->u.sys_property.div_mode);
reg = readl(clk->u.sys_property.cfgreg);
reg &= ~(clk->u.sys_property.div_mask << clk->u.sys_property.div_offset);
reg |= (div & clk->u.sys_property.div_mask) << clk->u.sys_property.div_offset;
writel(reg, clk->u.sys_property.cfgreg);
}
static void clk_sys_init(xClockProperty *clk)
{
uint32_t reg;
uint32_t val;
configASSERT(clk);
if (clk->u.sys_property.index_value >= 0 && clk->u.sys_property.index_mask) {
clk->source_index = clk->u.sys_property.index_value;
val = clk->u.sys_property.index_value == 3 ? 4 : clk->u.sys_property.index_value;
reg = readl(clk->u.sys_property.cfgreg);
reg &= ~(clk->u.sys_property.index_mask << clk->u.sys_property.index_offset);
reg |= (val & clk->u.sys_property.index_mask) << clk->u.sys_property.index_offset;
writel(reg, clk->u.sys_property.cfgreg);
} else if (clk->u.sys_property.index_mask) {
reg = readl(clk->u.sys_property.cfgreg);
val = (reg >> clk->u.sys_property.index_offset) & clk->u.sys_property.index_mask;
clk->source_index = val == 4 ? 3 : val;
}
if (clk->u.sys_property.div_value >= 0 && clk->u.sys_property.div_mask) {
val = clk_set_div(clk->u.sys_property.div_value, clk->u.sys_property.div_mode);
clk->div = clk_get_div(val, clk->u.sys_property.div_mode);
reg = readl(clk->u.sys_property.cfgreg);
reg &= ~(clk->u.sys_property.div_mask << clk->u.sys_property.div_offset);
reg |= (val & clk->u.sys_property.div_mask) << clk->u.sys_property.div_offset;
writel(reg, clk->u.sys_property.cfgreg);
} else if (clk->u.sys_property.div_mask) {
reg = readl(clk->u.sys_property.cfgreg);
val = (reg >> clk->u.sys_property.div_offset) & clk->u.sys_property.div_mask;
clk->div = clk_get_div(val, clk->u.sys_property.div_mode);
} else if (clk->u.sys_property.div_value > 0) {
clk->div = clk->u.sys_property.div_value;
} else {
clk->div = 1;
}
if (clk->u.sys_property.inv_reg) {
reg = readl(clk->u.sys_property.inv_reg);
reg &= ~(clk->u.sys_property.inv_mask << clk->u.sys_property.inv_offset);
reg |= (clk->u.sys_property.inv_value & clk->u.sys_property.inv_mask)
<< clk->u.sys_property.inv_offset;
writel(reg, clk->u.sys_property.inv_reg);
}
}
static void clk_sys_enable(xClockProperty *clk)
{
int i;
configASSERT(clk);
for (i = 0; i < clk->enable_bits; i++)
writel(readl(clk->enable_reg[i]) | (1 << clk->enable_offset[i]),
clk->enable_reg[i]);
}
static void clk_sys_disable(xClockProperty *clk)
{
int i;
configASSERT(clk);
for (i = 0; i < clk->enable_bits; i++)
writel(readl(clk->enable_reg[i]) & ~(1 << clk->enable_offset[i]),
clk->enable_reg[i]);
}
void vClkInit(void)
{
int i;
xClockProperty *clk;
for (i = 0; i < CLOCK_NUM; i++) {
clk = &xClocks[i];
if (clk->clktype == PLL_CLOCK) {
clk_pll_init(clk);
} else if (clk->clktype == SYS_CLOCK) {
clk_sys_init(clk);
}
}
}
uint32_t ulClkGetRate(uint32_t clkid)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return 0;
switch (clk->clktype) {
case FIXED_CLOCK:
return clk_fixed_get_rate(clk);
case FIXED_FACTOR_CLOCK:
return clk_fixed_factor_get_rate(clk);
case PLL_CLOCK:
return clk_pll_get_rate(clk);
case SYS_CLOCK:
return clk_sys_get_rate(clk);
}
return 0;
}
void vClkSetRate(uint32_t clkid, uint32_t freq)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return;
if (clk->clktype == SYS_CLOCK)
clk_sys_set_rate(clk, freq);
}
void vClkEnable(uint32_t clkid)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return;
if (clk->clktype == SYS_CLOCK)
clk_sys_enable(clk);
}
void vClkDisable(uint32_t clkid)
{
xClockProperty *clk = clk_get(clkid);
if (clk == NULL)
return;
if (clk->clktype == SYS_CLOCK)
clk_sys_disable(clk);
}