You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
12 KiB
438 lines
12 KiB
/* |
|
* Copyright (c) 2025 Renesas Electronics Corporation |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <stdint.h> |
|
#include <zephyr/kernel.h> |
|
#include <zephyr/arch/cpu.h> |
|
#include "clock_control_renesas_rza2m_cpg_lld.h" |
|
|
|
static const struct rza2m_stb_module_info gs_stbcr[] = { |
|
{MODULE_CORESIGHT, STBCR2_OFFSET, CPG_STBCR2_MSTP20}, |
|
{MODULE_OSTM0, STBCR3_OFFSET, CPG_STBCR3_MSTP36}, |
|
{MODULE_OSTM1, STBCR3_OFFSET, CPG_STBCR3_MSTP35}, |
|
{MODULE_OSTM2, STBCR3_OFFSET, CPG_STBCR3_MSTP34}, |
|
{MODULE_MTU3, STBCR3_OFFSET, CPG_STBCR3_MSTP33}, |
|
{MODULE_CANFD, STBCR3_OFFSET, CPG_STBCR3_MSTP32}, |
|
{MODULE_ADC, STBCR5_OFFSET, CPG_STBCR5_MSTP57}, |
|
{MODULE_GPT, STBCR3_OFFSET, CPG_STBCR3_MSTP30}, |
|
{MODULE_SCIFA0, STBCR4_OFFSET, CPG_STBCR4_MSTP47}, |
|
{MODULE_SCIFA1, STBCR4_OFFSET, CPG_STBCR4_MSTP46}, |
|
{MODULE_SCIFA2, STBCR4_OFFSET, CPG_STBCR4_MSTP45}, |
|
{MODULE_SCIFA3, STBCR4_OFFSET, CPG_STBCR4_MSTP44}, |
|
{MODULE_SCIFA4, STBCR4_OFFSET, CPG_STBCR4_MSTP43}, |
|
{MODULE_SCI0, STBCR4_OFFSET, CPG_STBCR4_MSTP42}, |
|
{MODULE_SCI1, STBCR4_OFFSET, CPG_STBCR4_MSTP41}, |
|
{MODULE_IrDA, STBCR4_OFFSET, CPG_STBCR4_MSTP40}, |
|
{MODULE_CEU, STBCR5_OFFSET, CPG_STBCR5_MSTP56}, |
|
{MODULE_RTC0, STBCR5_OFFSET, CPG_STBCR5_MSTP53}, |
|
{MODULE_RTC1, STBCR5_OFFSET, CPG_STBCR5_MSTP52}, |
|
{MODULE_JCU, STBCR5_OFFSET, CPG_STBCR5_MSTP51}, |
|
{MODULE_VIN, STBCR6_OFFSET, CPG_STBCR6_MSTP66}, |
|
{MODULE_ETHER, STBCR6_OFFSET, |
|
CPG_STBCR6_MSTP65 | CPG_STBCR6_MSTP64 | CPG_STBCR6_MSTP63 | CPG_STBCR6_MSTP62}, |
|
{MODULE_USB0, STBCR6_OFFSET, CPG_STBCR6_MSTP61}, |
|
{MODULE_USB1, STBCR6_OFFSET, CPG_STBCR6_MSTP60}, |
|
{MODULE_IMR2, STBCR7_OFFSET, CPG_STBCR7_MSTP77}, |
|
{MODULE_DRW, STBCR7_OFFSET, CPG_STBCR7_MSTP76}, |
|
{MODULE_MIPI, STBCR7_OFFSET, CPG_STBCR7_MSTP75}, |
|
{MODULE_SSIF0, STBCR7_OFFSET, CPG_STBCR7_MSTP73}, |
|
{MODULE_SSIF1, STBCR7_OFFSET, CPG_STBCR7_MSTP72}, |
|
{MODULE_SSIF2, STBCR7_OFFSET, CPG_STBCR7_MSTP71}, |
|
{MODULE_SSIF3, STBCR7_OFFSET, CPG_STBCR7_MSTP70}, |
|
{MODULE_I2C0, STBCR8_OFFSET, CPG_STBCR8_MSTP87}, |
|
{MODULE_I2C1, STBCR8_OFFSET, CPG_STBCR8_MSTP86}, |
|
{MODULE_I2C2, STBCR8_OFFSET, CPG_STBCR8_MSTP85}, |
|
{MODULE_I2C3, STBCR8_OFFSET, CPG_STBCR8_MSTP84}, |
|
{MODULE_SPIBSC, STBCR8_OFFSET, CPG_STBCR8_MSTP83}, |
|
{MODULE_VDC6, STBCR8_OFFSET, CPG_STBCR8_MSTP81}, |
|
{MODULE_RSPI0, STBCR9_OFFSET, CPG_STBCR9_MSTP97}, |
|
{MODULE_RSPI1, STBCR9_OFFSET, CPG_STBCR9_MSTP96}, |
|
{MODULE_RSPI2, STBCR9_OFFSET, CPG_STBCR9_MSTP95}, |
|
{MODULE_HYPERBUS, STBCR9_OFFSET, CPG_STBCR9_MSTP93}, |
|
{MODULE_OCTAMEM, STBCR9_OFFSET, CPG_STBCR9_MSTP92}, |
|
{MODULE_RSPDIF, STBCR9_OFFSET, CPG_STBCR9_MSTP91}, |
|
{MODULE_DRP, STBCR9_OFFSET, CPG_STBCR9_MSTP90}, |
|
{MODULE_TSIP, STBCR10_OFFSET, CPG_STBCR10_MSTP107}, |
|
{MODULE_NAND, STBCR10_OFFSET, CPG_STBCR10_MSTP104}, |
|
{MODULE_SDMMC0, STBCR10_OFFSET, CPG_STBCR10_MSTP103 | CPG_STBCR10_MSTP102}, |
|
{MODULE_SDMMC1, STBCR10_OFFSET, CPG_STBCR10_MSTP101 | CPG_STBCR10_MSTP100}, |
|
}; |
|
|
|
static const struct rza2m_stb_module_info gs_stbreq[] = { |
|
{MODULE_CORESIGHT, STBREQ1_OFFSET, CPG_STBREQ1_STBRQ15}, |
|
{MODULE_CEU, STBREQ1_OFFSET, CPG_STBREQ1_STBRQ10}, |
|
{MODULE_JCU, STBREQ1_OFFSET, CPG_STBREQ1_STBRQ13}, |
|
{MODULE_VIN, STBREQ2_OFFSET, CPG_STBREQ2_STBRQ27}, |
|
{MODULE_ETHER, STBREQ2_OFFSET, CPG_STBREQ2_STBRQ26}, |
|
{MODULE_USB0, STBREQ3_OFFSET, CPG_STBREQ3_STBRQ31 | CPG_STBREQ3_STBRQ30}, |
|
{MODULE_USB1, STBREQ3_OFFSET, CPG_STBREQ3_STBRQ33 | CPG_STBREQ3_STBRQ32}, |
|
{MODULE_IMR2, STBREQ2_OFFSET, CPG_STBREQ2_STBRQ23}, |
|
{MODULE_DRW, STBREQ2_OFFSET, CPG_STBREQ2_STBRQ21 | CPG_STBREQ2_STBRQ20}, |
|
{MODULE_VDC6, STBREQ2_OFFSET, CPG_STBREQ2_STBRQ25}, |
|
{MODULE_DRP, STBREQ2_OFFSET, CPG_STBREQ2_STBRQ24}, |
|
{MODULE_NAND, STBREQ2_OFFSET, CPG_STBREQ2_STBRQ22}, |
|
{MODULE_SDMMC0, STBREQ1_OFFSET, CPG_STBREQ1_STBRQ12}, |
|
{MODULE_SDMMC1, STBREQ1_OFFSET, CPG_STBREQ1_STBRQ11}, |
|
}; |
|
|
|
static const struct rza2m_stb_module_info gs_stback[] = { |
|
{MODULE_CORESIGHT, STBACK1_OFFSET, CPG_STBACK1_STBAK15}, |
|
{MODULE_CEU, STBACK1_OFFSET, CPG_STBACK1_STBAK10}, |
|
{MODULE_JCU, STBACK1_OFFSET, CPG_STBACK1_STBAK13}, |
|
{MODULE_VIN, STBACK2_OFFSET, CPG_STBACK2_STBAK27}, |
|
{MODULE_ETHER, STBACK2_OFFSET, CPG_STBACK2_STBAK26}, |
|
{MODULE_USB0, STBACK3_OFFSET, CPG_STBACK3_STBAK31 | CPG_STBACK3_STBAK30}, |
|
{MODULE_USB1, STBACK3_OFFSET, CPG_STBACK3_STBAK33 | CPG_STBACK3_STBAK32}, |
|
{MODULE_IMR2, STBACK2_OFFSET, CPG_STBACK2_STBAK23}, |
|
{MODULE_DRW, STBACK2_OFFSET, CPG_STBACK2_STBAK21 | CPG_STBACK2_STBAK20}, |
|
{MODULE_VDC6, STBACK2_OFFSET, CPG_STBACK2_STBAK25}, |
|
{MODULE_DRP, STBACK2_OFFSET, CPG_STBACK2_STBAK24}, |
|
{MODULE_NAND, STBACK2_OFFSET, CPG_STBACK2_STBAK22}, |
|
{MODULE_SDMMC0, STBACK1_OFFSET, CPG_STBACK1_STBAK12}, |
|
{MODULE_SDMMC1, STBACK1_OFFSET, CPG_STBACK1_STBAK11}, |
|
}; |
|
|
|
void rza2m_pl310_set_standby_mode(void) |
|
{ |
|
uint32_t reg_val; |
|
|
|
/* Read current register value */ |
|
reg_val = sys_read32(PL310_BASE_ADDR + PL310_PWR_CTRL_OFFSET); |
|
|
|
/* Set standby mode enable bit */ |
|
reg_val |= BIT(PL310_PWR_CTRL_STANDBY_MODE_EN_SHIFT); |
|
|
|
sys_write32(reg_val, PL310_BASE_ADDR + PL310_PWR_CTRL_OFFSET); |
|
sys_read32(PL310_BASE_ADDR + PL310_PWR_CTRL_OFFSET); |
|
} |
|
|
|
void rza2m_cpg_calculate_pll_frequency(const struct device *dev) |
|
{ |
|
const struct rza2m_cpg_clock_config *config = dev->config; |
|
struct rza2m_cpg_clock_data *data = dev->data; |
|
|
|
data->cpg_extal_frequency_hz = config->cpg_extal_freq_hz_cfg; |
|
|
|
if ((data->cpg_extal_frequency_hz >= RZA2M_CPG_MHZ(10)) && |
|
(data->cpg_extal_frequency_hz <= RZA2M_CPG_MHZ(12))) { |
|
data->cpg_pll_frequency_hz = data->cpg_extal_frequency_hz * 88; |
|
} else if ((data->cpg_extal_frequency_hz >= RZA2M_CPG_MHZ(20)) && |
|
(data->cpg_extal_frequency_hz <= RZA2M_CPG_MHZ(24))) { |
|
data->cpg_pll_frequency_hz = data->cpg_extal_frequency_hz * 44; |
|
} |
|
} |
|
|
|
static void rza2m_cpg_change_reg_bits(mem_addr_t reg, uint8_t bitmask, bool enable) |
|
{ |
|
uint8_t reg_val; |
|
|
|
reg_val = sys_read8(reg); |
|
|
|
if (enable) { |
|
reg_val &= ~bitmask; |
|
} else { |
|
reg_val |= bitmask; |
|
} |
|
|
|
sys_write8(reg_val, reg); |
|
sys_read8(reg); |
|
} |
|
|
|
static const struct rza2m_stb_module_info *rza2m_cpg_get_stbcr_info(enum rza2m_stb_module module) |
|
{ |
|
size_t index; |
|
size_t count = ARRAY_SIZE(gs_stbcr); |
|
|
|
for (index = 0; index < count; index++) { |
|
if (module == gs_stbcr[index].module) { |
|
return &gs_stbcr[index]; |
|
} |
|
} |
|
|
|
/* Return NULL if not found */ |
|
return NULL; |
|
} |
|
|
|
static const struct rza2m_stb_module_info *rza2m_cpg_get_stbreq_info(enum rza2m_stb_module module) |
|
{ |
|
size_t index; |
|
size_t count = ARRAY_SIZE(gs_stbreq); |
|
|
|
for (index = 0; index < count; index++) { |
|
if (module == gs_stbreq[index].module) { |
|
return &gs_stbreq[index]; |
|
} |
|
} |
|
|
|
/* Return NULL if not found */ |
|
return NULL; |
|
} |
|
|
|
static const struct rza2m_stb_module_info *rza2m_cpg_get_stback_info(enum rza2m_stb_module module) |
|
{ |
|
size_t index; |
|
size_t count = ARRAY_SIZE(gs_stback); |
|
|
|
for (index = 0; index < count; index++) { |
|
if (module == gs_stback[index].module) { |
|
return &gs_stback[index]; |
|
} |
|
} |
|
|
|
/* Return NULL if not found */ |
|
return NULL; |
|
} |
|
|
|
static uint8_t rza2m_cpg_wait_bit_val(uint32_t reg_addr, uint8_t bit_mask, uint8_t bits_val, |
|
int32_t us_wait) |
|
{ |
|
uint8_t reg_val; |
|
int32_t wait_cnt = (us_wait / 5); |
|
|
|
do { |
|
reg_val = sys_read8(reg_addr) & bit_mask; |
|
|
|
if (reg_val == bits_val) { |
|
break; |
|
} |
|
|
|
if (wait_cnt > 0) { |
|
k_busy_wait(5); |
|
} |
|
} while (wait_cnt-- > 0); |
|
|
|
return reg_val; |
|
} |
|
|
|
int rza2m_cpg_mstp_clock_endisable(const struct device *dev, enum rza2m_stb_module module, |
|
bool enable) |
|
{ |
|
uint8_t reg_val = 0; |
|
const struct rza2m_stb_module_info *p_stbcr; |
|
const struct rza2m_stb_module_info *p_stbreq; |
|
const struct rza2m_stb_module_info *p_stback; |
|
|
|
/* Obtain the STBCR information */ |
|
p_stbcr = rza2m_cpg_get_stbcr_info(module); |
|
|
|
/* Check if unsupported module */ |
|
if (NULL != p_stbcr) { |
|
rza2m_cpg_change_reg_bits(CPG_REG_ADDR(p_stbcr->reg_offset), p_stbcr->mask, enable); |
|
} else { |
|
return -ENOTSUP; |
|
} |
|
|
|
p_stbreq = rza2m_cpg_get_stbreq_info(module); |
|
p_stback = rza2m_cpg_get_stback_info(module); |
|
|
|
if ((NULL != p_stback) && (NULL != p_stbreq)) { |
|
rza2m_cpg_change_reg_bits(CPG_REG_ADDR(p_stbreq->reg_offset), p_stbreq->mask, |
|
enable); |
|
reg_val = rza2m_cpg_wait_bit_val(CPG_REG_ADDR(p_stback->reg_offset), p_stbreq->mask, |
|
0, STBACK_REG_WAIT_US); |
|
if (reg_val) { |
|
return -EIO; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int rza2m_cpg_modify_frqcr(const struct device *dev, enum rza2m_cp_sub_clock clk_sub_src, |
|
uint32_t sub_clk_frequency_hz, uint16_t *p_frqcr) |
|
{ |
|
struct rza2m_cpg_clock_data *data = dev->data; |
|
uint16_t div_d; |
|
uint16_t fc; |
|
|
|
/* Avoid divide by zero */ |
|
if (sub_clk_frequency_hz == 0) { |
|
return -EINVAL; |
|
} |
|
|
|
div_d = data->cpg_pll_frequency_hz / sub_clk_frequency_hz; |
|
|
|
if (CPG_SUB_CLOCK_ICLK == clk_sub_src) { |
|
if (div_d == 2) { |
|
fc = 0; |
|
} else if (div_d == 4) { |
|
fc = 1; |
|
} else if (div_d == 8) { |
|
fc = 2; |
|
} else if (div_d == 16) { |
|
fc = 3; |
|
} else { |
|
return -EINVAL; |
|
} |
|
/* Clear IFC bit */ |
|
(*p_frqcr) &= (uint16_t)(~CPG_FRQCR_IFC); |
|
|
|
/* Modify IFC bit */ |
|
(*p_frqcr) |= (uint16_t)(fc << CPG_FRQCR_IFC_SHIFT); |
|
} else if (CPG_SUB_CLOCK_BCLK == clk_sub_src) { |
|
if (div_d == 8) { |
|
fc = 1; |
|
} else if (div_d == 16) { |
|
fc = 2; |
|
} else if (div_d == 32) { |
|
fc = 3; |
|
} else { |
|
return -EINVAL; |
|
} |
|
|
|
/* Clear BFC bit */ |
|
(*p_frqcr) &= (uint16_t)(~CPG_FRQCR_BFC); |
|
|
|
/* Modify BFC bit */ |
|
(*p_frqcr) |= (uint16_t)(fc << CPG_FRQCR_BFC_SHIFT); |
|
} else if (CPG_SUB_CLOCK_P1CLK == clk_sub_src) { |
|
if (div_d == 16) { |
|
fc = 2; |
|
} else if (div_d == 32) { |
|
fc = 3; |
|
} else { |
|
return -EINVAL; |
|
} |
|
|
|
/* Clear PFC bit */ |
|
(*p_frqcr) &= (uint16_t)(~CPG_FRQCR_PFC); |
|
|
|
/* Modify PFC bit */ |
|
(*p_frqcr) |= (uint16_t)(fc << CPG_FRQCR_PFC_SHIFT); |
|
} else { |
|
return -ENOTSUP; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
int rza2m_cpg_set_sub_clock_divider(const struct device *dev, enum rza2m_cp_sub_clock clk_sub_src, |
|
uint32_t sub_clk_frequency_hz) |
|
{ |
|
struct rza2m_cpg_clock_data *data = dev->data; |
|
uint16_t frqcr; |
|
uint16_t check_frqcr; |
|
int ret; |
|
|
|
/* Read previous settings */ |
|
frqcr = sys_read16(CPG_REG_ADDR(CPG_FRQCR_OFFSET)); |
|
|
|
ret = rza2m_cpg_modify_frqcr(dev, clk_sub_src, sub_clk_frequency_hz, &frqcr); |
|
|
|
if (ret < 0) { |
|
return ret; |
|
} |
|
|
|
/* Check unless valid combination */ |
|
check_frqcr = frqcr & (CPG_FRQCR_IFC | CPG_FRQCR_BFC | CPG_FRQCR_PFC); |
|
switch (check_frqcr) { |
|
case 0x012: /* "VALID":do nothing, fall through */ |
|
case 0x112: /* "VALID":do nothing, fall through */ |
|
case 0x212: /* "VALID":do nothing, fall through */ |
|
case 0x322: /* "VALID":do nothing, fall through */ |
|
case 0x333: /* "VALID":do nothing, fall through */ |
|
break; |
|
default: |
|
return !EINVAL; |
|
} |
|
|
|
/* Update local divisor variables based on the clock type */ |
|
switch (clk_sub_src) { |
|
case CPG_SUB_CLOCK_ICLK: |
|
switch ((frqcr & CPG_FRQCR_IFC) >> CPG_FRQCR_IFC_SHIFT) { |
|
case 0: |
|
data->cpg_iclk_divisor = 2; |
|
break; |
|
case 1: |
|
data->cpg_iclk_divisor = 4; |
|
break; |
|
case 2: |
|
data->cpg_iclk_divisor = 8; |
|
break; |
|
case 3: |
|
data->cpg_iclk_divisor = 16; |
|
break; |
|
default: |
|
break; |
|
} |
|
data->cpg_iclk_frequency_hz = data->cpg_pll_frequency_hz / data->cpg_iclk_divisor; |
|
break; |
|
|
|
case CPG_SUB_CLOCK_BCLK: |
|
switch ((frqcr & CPG_FRQCR_BFC) >> CPG_FRQCR_BFC_SHIFT) { |
|
case 1: |
|
data->cpg_bclk_divisor = 8; |
|
break; |
|
case 2: |
|
data->cpg_bclk_divisor = 16; |
|
break; |
|
case 3: |
|
data->cpg_bclk_divisor = 32; |
|
break; |
|
default: |
|
break; |
|
} |
|
data->cpg_bclk_frequency_hz = data->cpg_pll_frequency_hz / data->cpg_bclk_divisor; |
|
break; |
|
|
|
case CPG_SUB_CLOCK_P1CLK: |
|
switch ((frqcr & CPG_FRQCR_PFC) >> CPG_FRQCR_PFC_SHIFT) { |
|
case 2: |
|
data->cpg_p1clk_divisor = 16; |
|
break; |
|
case 3: |
|
data->cpg_p1clk_divisor = 32; |
|
break; |
|
default: |
|
break; |
|
} |
|
data->cpg_p1clk_frequency_hz = data->cpg_pll_frequency_hz / data->cpg_p1clk_divisor; |
|
break; |
|
|
|
default: |
|
return -ENOTSUP; |
|
} |
|
|
|
rza2m_pl310_set_standby_mode(); |
|
sys_write16(frqcr, CPG_REG_ADDR(CPG_FRQCR_OFFSET)); |
|
sys_read16(CPG_REG_ADDR(CPG_FRQCR_OFFSET)); |
|
|
|
return 0; |
|
} |
|
|
|
int rza2m_cpg_get_clock(const struct device *dev, enum rza2m_cpg_get_freq_src src, uint32_t *p_freq) |
|
{ |
|
struct rza2m_cpg_clock_data *data = dev->data; |
|
|
|
if (NULL == p_freq) { |
|
return -EINVAL; |
|
} |
|
|
|
switch (src) { |
|
case CPG_FREQ_EXTAL: |
|
*p_freq = data->cpg_extal_frequency_hz; |
|
break; |
|
case CPG_FREQ_ICLK: |
|
*p_freq = data->cpg_iclk_frequency_hz; |
|
break; |
|
case CPG_FREQ_GCLK: |
|
*p_freq = (data->cpg_pll_frequency_hz * 2) / data->cpg_bclk_divisor; |
|
break; |
|
case CPG_FREQ_BCLK: |
|
*p_freq = data->cpg_bclk_frequency_hz; |
|
break; |
|
case CPG_FREQ_P1CLK: |
|
*p_freq = data->cpg_p1clk_frequency_hz; |
|
break; |
|
case CPG_FREQ_P0CLK: |
|
*p_freq = data->cpg_pll_frequency_hz / 32; |
|
break; |
|
default: |
|
return -ENOTSUP; |
|
} |
|
|
|
return 0; |
|
}
|
|
|