/*
* Copyright (c) 2018 Synopsys, Inc. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc.h"
#include "sysconf.h"
#define PLL_CLK_IN (SYSCLK_DEFAULT_IOSC_HZ / 1000000) /* PLL clock in */
#define sysconf_reg_ptr ((sysconf_reg_t *)(BASE_ADDR_SYSCONFIG))
typedef struct pll_conf {
uint32_t fout;
uint32_t pll;
} pll_conf_t;
#define PLL_CONF_VAL(n, m, od) \
(((n) << PLLCON_BIT_OFFSET_N) | \
((m) << (PLLCON_BIT_OFFSET_M)) | \
((od) << PLLCON_BIT_OFFSET_OD))
/* the following configuration is based on Fin = 16 Mhz */
static const pll_conf_t pll_configuration[] = {
{100, PLL_CONF_VAL(1, 25, 2)}, /* 100 Mhz */
{50, PLL_CONF_VAL(1, 25, 3)}, /* 50 Mhz */
{150, PLL_CONF_VAL(4, 75, 1)}, /* 150 Mhz */
{75, PLL_CONF_VAL(4, 75, 2)}, /* 75 Mhz */
{25, PLL_CONF_VAL(2, 25, 3)}, /* 25 Mhz */
{72, PLL_CONF_VAL(8, 144, 2)}, /* 72 Mhz */
{144, PLL_CONF_VAL(8, 144, 1)}, /* 144 Mhz */
};
/**
* PLL Fout = Fin * M/ (N *n NO)
*
* Fref = Fin / N; Fvco = Fref * M Fout = Fvco / NO
*
* N = input divider value (1, 2, 3 … 15)
* M = feedback divider value (4, 5, 6 … 16383)
* NO = output divider value (1, 2, 4, or 8)
*
* 1 Mhz <= Fref <= 50 Mhz
* 200 Mhz <= Fvco <= 400 Mhz
*
*/
void arc_iot_pll_conf_reg(uint32_t val)
{
sysconf_reg_ptr->CLKSEL = CLKSEL_EXT_16M;
/* 0x52000000 is not described in spec. */
sysconf_reg_ptr->PLLCON = val | (0x52000000);
sysconf_reg_ptr->PLLCON = val | (1 << PLLCON_BIT_OFFSET_PLLRST);
sysconf_reg_ptr->PLLCON = val & (~(1 << PLLCON_BIT_OFFSET_PLLRST));
while (!(sysconf_reg_ptr->PLLSTAT & (1 << PLLSTAT_BIT_OFFSET_PLLSTB)))
;
sysconf_reg_ptr->CLKSEL = CLKSEL_PLL;
/* from AHB_CLK_DIVIDER, not from DVFSS&PMC */
sysconf_reg_ptr->AHBCLKDIV_SEL |= 1;
/* AHB clk divisor = 1 */
sysconf_reg_ptr->AHBCLKDIV = 0x1;
}
int32_t arc_iot_pll_fout_config(uint32_t freq)
{
uint32_t i;
if (freq == PLL_CLK_IN) {
sysconf_reg_ptr->CLKSEL = CLKSEL_EXT_16M;
}
for (i = 0U; i < ARRAY_SIZE(pll_configuration); i++) {
if (pll_configuration[i].fout == freq) {
break;
}
}
if (i >= ARRAY_SIZE(pll_configuration)) {
return -1;
}
/* config eflash clk, must be < 100 Mhz */
if (freq > 100) {
arc_iot_eflash_clk_div(2);
} else {
arc_iot_eflash_clk_div(1);
}
arc_iot_pll_conf_reg(pll_configuration[i].pll);
return 0;
}
void arc_iot_ahb_clk_divisor(uint8_t div)
{
sysconf_reg_ptr->AHBCLKDIV = div;
}
void arc_iot_ahb_clk_enable(uint8_t dev)
{
if (dev > AHBCLKEN_BIT_SDIO) {
return;
}
sysconf_reg_ptr->AHBCLKEN |= (1 << dev);
}
void arc_iot_ahb_clk_disable(uint8_t dev)
{
if (dev > AHBCLKEN_BIT_SDIO) {
return;
}
sysconf_reg_ptr->AHBCLKEN &= (~(1 << dev));
}
void arc_iot_apb_clk_divisor(uint8_t div)
{
sysconf_reg_ptr->APBCLKDIV = div;
}
void arc_iot_apb_clk_enable(uint8_t dev)
{
if (dev > APBCLKEN_BIT_I3C) {
return;
}
sysconf_reg_ptr->APBCLKEN |= (1 << dev);
}
void arc_iot_apb_clk_disable(uint8_t dev)
{
if (dev > APBCLKEN_BIT_I3C) {
return;
}
sysconf_reg_ptr->APBCLKEN &= (~(1 << dev));
}
void arc_iot_dio_clk_divisor(uint8_t div)
{
sysconf_reg_ptr->SDIO_REFCLK_DIV;
}
void arc_iot_spi_master_clk_divisor(uint8_t id, uint8_t div)
{
if (id == SPI_MASTER_0) {
sysconf_reg_ptr->SPI_MST_CLKDIV =
(sysconf_reg_ptr->SPI_MST_CLKDIV & 0xffffff00) | div;
} else if (id == SPI_MASTER_1) {
sysconf_reg_ptr->SPI_MST_CLKDIV =
(sysconf_reg_ptr->SPI_MST_CLKDIV & 0xffff00ff) | (div << 8);
} else if (id == SPI_MASTER_2) {
sysconf_reg_ptr->SPI_MST_CLKDIV =
(sysconf_reg_ptr->SPI_MST_CLKDIV & 0xff00ffff) | (div << 16);
}
}
void arc_iot_gpio8b_dbclk_div(uint8_t bank, uint8_t div)
{
if (bank == GPIO8B_BANK0) {
sysconf_reg_ptr->GPIO8B_DBCLK_DIV =
(sysconf_reg_ptr->GPIO8B_DBCLK_DIV & 0xffffff00) | div;
} else if (bank == GPIO8B_BANK1) {
sysconf_reg_ptr->GPIO8B_DBCLK_DIV =
(sysconf_reg_ptr->GPIO8B_DBCLK_DIV & 0xffff00ff) | (div << 8);
} else if (bank == GPIO8B_BANK2) {
sysconf_reg_ptr->GPIO8B_DBCLK_DIV =
(sysconf_reg_ptr->GPIO8B_DBCLK_DIV & 0xff00ffff) | (div << 16);
} else if (bank == GPIO8B_BANK3) {
sysconf_reg_ptr->GPIO8B_DBCLK_DIV =
(sysconf_reg_ptr->GPIO8B_DBCLK_DIV & 0x00ffffff) | (div << 24);
}
}
void arc_iot_gpio4b_dbclk_div(uint8_t bank, uint8_t div)
{
if (bank == GPIO4B_BANK0) {
sysconf_reg_ptr->GPIO4B_DBCLK_DIV =
(sysconf_reg_ptr->GPIO4B_DBCLK_DIV & 0xffffff00) | div;
} else if (bank == GPIO4B_BANK1) {
sysconf_reg_ptr->GPIO4B_DBCLK_DIV =
(sysconf_reg_ptr->GPIO4B_DBCLK_DIV & 0xffff00ff) | (div << 8);
} else if (bank == GPIO4B_BANK2) {
sysconf_reg_ptr->GPIO4B_DBCLK_DIV =
(sysconf_reg_ptr->GPIO4B_DBCLK_DIV & 0xff00ffff) | (div << 16);
}
}
void arc_iot_i2s_tx_clk_div(uint8_t div)
{
sysconf_reg_ptr->I2S_TX_SCLKDIV = div;
}
void arc_iot_i2s_rx_clk_div(uint8_t div)
{
sysconf_reg_ptr->I2S_RX_SCLKDIV = div;
}
void arc_iot_i2s_rx_clk_sel(uint8_t sel)
{
sysconf_reg_ptr->I2S_RX_SCLKSEL = sel;
}
void arc_iot_syscon_reset(void)
{
sysconf_reg_ptr->RSTCON = 0x55AA6699;
}
uint32_t arc_iot_is_poweron_rst(void)
{
if (sysconf_reg_ptr->RSTSTAT & SYS_RST_SOFTWARE_ON) {
return 0;
} else {
return 1;
}
}
void arc_iot_dvfs_clk_divisor(uint8_t level, uint8_t div)
{
if (level == DVFS_PERF_LEVEL0) {
sysconf_reg_ptr->DVFS_CLKDIV =
(sysconf_reg_ptr->DVFS_CLKDIV & 0xffffff00) | div;
} else if (level == DVFS_PERF_LEVEL1) {
sysconf_reg_ptr->DVFS_CLKDIV =
(sysconf_reg_ptr->DVFS_CLKDIV & 0xffff00ff) | (div << 8);
} else if (level == DVFS_PERF_LEVEL2) {
sysconf_reg_ptr->DVFS_CLKDIV =
(sysconf_reg_ptr->DVFS_CLKDIV & 0xff00ffff) | (div << 16);
} else if (level == DVFS_PERF_LEVEL3) {
sysconf_reg_ptr->DVFS_CLKDIV =
(sysconf_reg_ptr->DVFS_CLKDIV & 0x00ffffff) | (div << 24);
}
}
void arc_iot_dvfs_vdd_config(uint8_t level, uint8_t val)
{
val &= 0xf;
if (level == DVFS_PERF_LEVEL0) {
sysconf_reg_ptr->DVFS_VDDSET =
(sysconf_reg_ptr->DVFS_VDDSET & 0xfffffff0) | val;
} else if (level == DVFS_PERF_LEVEL1) {
sysconf_reg_ptr->DVFS_VDDSET =
(sysconf_reg_ptr->DVFS_VDDSET & 0xffffff0f) | (val << 4);
} else if (level == DVFS_PERF_LEVEL2) {
sysconf_reg_ptr->DVFS_VDDSET =
(sysconf_reg_ptr->DVFS_VDDSET & 0xfffff0ff) | (val << 8);
} else if (level == DVFS_PERF_LEVEL3) {
sysconf_reg_ptr->DVFS_CLKDIV =
(sysconf_reg_ptr->DVFS_CLKDIV & 0xffff0fff) | (val << 12);
}
}
void arc_iot_dvfs_vwtime_config(uint8_t time)
{
sysconf_reg_ptr->DVFS_VWTIME = time;
}
void arc_iot_pmc_pwwtime_config(uint8_t time)
{
sysconf_reg_ptr->PMC_PUWTIME = time;
}
void arc_iot_uart3_clk_divisor(uint8_t div)
{
sysconf_reg_ptr->UART3SCLK_DIV = div;
}
void arc_iot_reset_powerdown_vector(uint32_t addr)
{
sysconf_reg_ptr->RESET_PD_VECTOR = addr;
}
void arc_iot_pwm_timer_pause(uint32_t id, uint32_t pause)
{
uint32_t val = sysconf_reg_ptr->TIMER_PAUSE;
if (id > PWM_TIMER5) {
return;
}
if (pause) {
val |= (1 << id);
} else {
val &= (~(1 << id));
}
sysconf_reg_ptr->TIMER_PAUSE = val;
}
void arc_iot_eflash_clk_div(uint8_t div)
{
sysconf_reg_ptr->AHBCLKDIV |= (div << 8);
}