| /* |
| * Copyright (c) 2018 Synopsys, Inc. All rights reserved. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/devicetree.h> |
| #include "sysconf.h" |
| |
| /* default system clock */ |
| #define SYSCLK_DEFAULT_IOSC_HZ MHZ(16) |
| |
| #define PLL_CLK_IN (SYSCLK_DEFAULT_IOSC_HZ / 1000000) /* PLL clock in */ |
| |
| |
| #define sysconf_reg_ptr ((sysconf_reg_t *)(DT_REG_ADDR(DT_NODELABEL(sysconf)))) |
| |
| |
| 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); |
| } |