/* * Copyright (c) 2022 Teslabs Engineering S.L. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT gd_gd32_cctl #include #include #include #include #include #include #include /** RCU offset (from id cell) */ #define GD32_CLOCK_ID_OFFSET(id) (((id) >> 6U) & 0xFFU) /** RCU configuration bit (from id cell) */ #define GD32_CLOCK_ID_BIT(id) ((id)&0x1FU) #define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) /** AHB prescaler exponents */ static const uint8_t ahb_exp[16] = { 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, 6U, 7U, 8U, 9U, }; /** APB1 prescaler exponents */ static const uint8_t apb1_exp[8] = { 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, }; /** APB2 prescaler exponents */ static const uint8_t apb2_exp[8] = { 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, }; struct clock_control_gd32_config { uint32_t base; }; #if DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer) /* timer identifiers */ #define TIMER_ID_OR_NONE(nodelabel) \ COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_NODELABEL(nodelabel)), \ (DT_CLOCKS_CELL(DT_NODELABEL(nodelabel), id),), ()) static const uint16_t timer_ids[] = { TIMER_ID_OR_NONE(timer0) /* */ TIMER_ID_OR_NONE(timer1) /* */ TIMER_ID_OR_NONE(timer2) /* */ TIMER_ID_OR_NONE(timer3) /* */ TIMER_ID_OR_NONE(timer4) /* */ TIMER_ID_OR_NONE(timer5) /* */ TIMER_ID_OR_NONE(timer6) /* */ TIMER_ID_OR_NONE(timer7) /* */ TIMER_ID_OR_NONE(timer8) /* */ TIMER_ID_OR_NONE(timer9) /* */ TIMER_ID_OR_NONE(timer10) /* */ TIMER_ID_OR_NONE(timer11) /* */ TIMER_ID_OR_NONE(timer12) /* */ TIMER_ID_OR_NONE(timer13) /* */ TIMER_ID_OR_NONE(timer14) /* */ TIMER_ID_OR_NONE(timer15) /* */ TIMER_ID_OR_NONE(timer16) /* */ }; #endif /* DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer) */ static int clock_control_gd32_on(const struct device *dev, clock_control_subsys_t sys) { const struct clock_control_gd32_config *config = dev->config; uint16_t id = *(uint16_t *)sys; sys_set_bit(config->base + GD32_CLOCK_ID_OFFSET(id), GD32_CLOCK_ID_BIT(id)); return 0; } static int clock_control_gd32_off(const struct device *dev, clock_control_subsys_t sys) { const struct clock_control_gd32_config *config = dev->config; uint16_t id = *(uint16_t *)sys; sys_clear_bit(config->base + GD32_CLOCK_ID_OFFSET(id), GD32_CLOCK_ID_BIT(id)); return 0; } static int clock_control_gd32_get_rate(const struct device *dev, clock_control_subsys_t sys, uint32_t *rate) { const struct clock_control_gd32_config *config = dev->config; uint16_t id = *(uint16_t *)sys; uint32_t cfg; uint8_t psc; cfg = sys_read32(config->base + RCU_CFG0_OFFSET); switch (GD32_CLOCK_ID_OFFSET(id)) { #if defined(CONFIG_SOC_SERIES_GD32F4XX) case RCU_AHB1EN_OFFSET: case RCU_AHB2EN_OFFSET: case RCU_AHB3EN_OFFSET: #else case RCU_AHBEN_OFFSET: #endif psc = (cfg & RCU_CFG0_AHBPSC_MSK) >> RCU_CFG0_AHBPSC_POS; *rate = CPU_FREQ >> ahb_exp[psc]; break; case RCU_APB1EN_OFFSET: #if !defined(CONFIG_SOC_SERIES_GD32VF103) && \ !defined(CONFIG_SOC_SERIES_GD32A50X) && \ !defined(CONFIG_SOC_SERIES_GD32L23X) case RCU_ADDAPB1EN_OFFSET: #endif psc = (cfg & RCU_CFG0_APB1PSC_MSK) >> RCU_CFG0_APB1PSC_POS; *rate = CPU_FREQ >> apb1_exp[psc]; break; case RCU_APB2EN_OFFSET: psc = (cfg & RCU_CFG0_APB2PSC_MSK) >> RCU_CFG0_APB2PSC_POS; *rate = CPU_FREQ >> apb2_exp[psc]; break; default: return -ENOTSUP; } #if DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer) /* handle timer clocks */ for (size_t i = 0U; i < ARRAY_SIZE(timer_ids); i++) { if (id != timer_ids[i]) { continue; } #if defined(CONFIG_SOC_SERIES_GD32F4XX) uint32_t cfg1 = sys_read32(config->base + RCU_CFG1_OFFSET); /* * The TIMERSEL bit in RCU_CFG1 controls the clock frequency of * all the timers connected to the APB1 and APB2 domains. * * Up to a certain threshold value of APB{1,2} prescaler, timer * clock equals to CK_AHB. This threshold value depends on * TIMERSEL setting (2 if TIMERSEL=0, 4 if TIMERSEL=1). Above * threshold, timer clock is set to a multiple of the APB * domain clock CK_APB{1,2} (2 if TIMERSEL=0, 4 if TIMERSEL=1). */ /* TIMERSEL = 0 */ if ((cfg1 & RCU_CFG1_TIMERSEL_MSK) == 0U) { if (psc <= 2U) { *rate = CPU_FREQ; } else { *rate *= 2U; } /* TIMERSEL = 1 */ } else { if (psc <= 4U) { *rate = CPU_FREQ; } else { *rate *= 4U; } } #else /* * If the APB prescaler equals 1, the timer clock frequencies * are set to the same frequency as that of the APB domain. * Otherwise, they are set to twice the frequency of the APB * domain. */ if (psc != 1U) { *rate *= 2U; } #endif /* CONFIG_SOC_SERIES_GD32F4XX */ } #endif /* DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer) */ return 0; } static enum clock_control_status clock_control_gd32_get_status(const struct device *dev, clock_control_subsys_t sys) { const struct clock_control_gd32_config *config = dev->config; uint16_t id = *(uint16_t *)sys; if (sys_test_bit(config->base + GD32_CLOCK_ID_OFFSET(id), GD32_CLOCK_ID_BIT(id)) != 0) { return CLOCK_CONTROL_STATUS_ON; } return CLOCK_CONTROL_STATUS_OFF; } static const struct clock_control_driver_api clock_control_gd32_api = { .on = clock_control_gd32_on, .off = clock_control_gd32_off, .get_rate = clock_control_gd32_get_rate, .get_status = clock_control_gd32_get_status, }; static const struct clock_control_gd32_config config = { .base = DT_REG_ADDR(DT_INST_PARENT(0)), }; DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, &config, PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &clock_control_gd32_api);