/* * Copyright (c) 2016 Linaro Limited. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT arm_beetle_syscon /** * @file * @brief Driver for Clock Control of Beetle MCUs. * * This file contains the Clock Control driver implementation for the * Beetle MCUs. */ #include #include #include #include #include #define MAINCLK_BASE_FREQ 24000000 struct beetle_clock_control_cfg_t { /* Clock Control ID */ uint32_t clock_control_id; /* Clock control freq */ uint32_t freq; }; static inline void beetle_set_clock(volatile uint32_t *base, uint8_t bit, enum arm_soc_state_t state) { uint32_t key; key = irq_lock(); switch (state) { case SOC_ACTIVE: base[0] |= (1 << bit); break; case SOC_SLEEP: base[2] |= (1 << bit); break; case SOC_DEEPSLEEP: base[4] |= (1 << bit); break; default: break; } irq_unlock(key); } static inline void beetle_ahb_set_clock_on(uint8_t bit, enum arm_soc_state_t state) { beetle_set_clock((volatile uint32_t *)&(__BEETLE_SYSCON->ahbclkcfg0set), bit, state); } static inline void beetle_ahb_set_clock_off(uint8_t bit, enum arm_soc_state_t state) { beetle_set_clock((volatile uint32_t *)&(__BEETLE_SYSCON->ahbclkcfg0clr), bit, state); } static inline void beetle_apb_set_clock_on(uint8_t bit, enum arm_soc_state_t state) { beetle_set_clock((volatile uint32_t *)&(__BEETLE_SYSCON->apbclkcfg0set), bit, state); } static inline void beetle_apb_set_clock_off(uint8_t bit, enum arm_soc_state_t state) { beetle_set_clock((volatile uint32_t *)&(__BEETLE_SYSCON->apbclkcfg0clr), bit, state); } static inline int beetle_clock_control_on(const struct device *dev, clock_control_subsys_t sub_system) { struct arm_clock_control_t *beetle_cc = (struct arm_clock_control_t *)(sub_system); uint8_t bit = 0U; switch (beetle_cc->bus) { case CMSDK_AHB: bit = (beetle_cc->device - _BEETLE_AHB_BASE) >> 12; beetle_ahb_set_clock_on(bit, beetle_cc->state); break; case CMSDK_APB: bit = (beetle_cc->device - _BEETLE_APB_BASE) >> 12; beetle_apb_set_clock_on(bit, beetle_cc->state); break; default: break; } return 0; } static inline int beetle_clock_control_off(const struct device *dev, clock_control_subsys_t sub_system) { struct arm_clock_control_t *beetle_cc = (struct arm_clock_control_t *)(sub_system); uint8_t bit = 0U; switch (beetle_cc->bus) { case CMSDK_AHB: bit = (beetle_cc->device - _BEETLE_AHB_BASE) >> 12; beetle_ahb_set_clock_off(bit, beetle_cc->state); break; case CMSDK_APB: bit = (beetle_cc->device - _BEETLE_APB_BASE) >> 12; beetle_apb_set_clock_off(bit, beetle_cc->state); break; default: break; } return 0; } static int beetle_clock_control_get_subsys_rate(const struct device *clock, clock_control_subsys_t sub_system, uint32_t *rate) { #ifdef CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL const struct beetle_clock_control_cfg_t * const cfg = clock->config; uint32_t nc_mainclk = beetle_round_freq(cfg->freq); *rate = nc_mainclk; #else ARG_UNUSED(clock); ARG_UNUSED(sub_system); *rate = MAINCLK_BASE_FREQ; #endif /* CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL */ return 0; } static const struct clock_control_driver_api beetle_clock_control_api = { .on = beetle_clock_control_on, .off = beetle_clock_control_off, .get_rate = beetle_clock_control_get_subsys_rate, }; #ifdef CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL static uint32_t beetle_round_freq(uint32_t mainclk) { uint32_t nc_mainclk = 0U; /* * Verify that the frequency is in the supported range otherwise * round it to the next closer one. */ if (mainclk <= BEETLE_PLL_FREQUENCY_12MHZ) { nc_mainclk = BEETLE_PLL_FREQUENCY_12MHZ; } else if (mainclk <= BEETLE_PLL_FREQUENCY_24MHZ) { nc_mainclk = BEETLE_PLL_FREQUENCY_24MHZ; } else if (mainclk <= BEETLE_PLL_FREQUENCY_36MHZ) { nc_mainclk = BEETLE_PLL_FREQUENCY_36MHZ; } else { nc_mainclk = BEETLE_PLL_FREQUENCY_48MHZ; } return nc_mainclk; } static uint32_t beetle_get_prescaler(uint32_t mainclk) { uint32_t pre_mainclk = 0U; /* * Verify that the frequency is in the supported range otherwise * round it to the next closer one. */ if (mainclk <= BEETLE_PLL_FREQUENCY_12MHZ) { pre_mainclk = BEETLE_PLL_PRESCALER_12MHZ; } else if (mainclk <= BEETLE_PLL_FREQUENCY_24MHZ) { pre_mainclk = BEETLE_PLL_PRESCALER_24MHZ; } else if (mainclk <= BEETLE_PLL_FREQUENCY_36MHZ) { pre_mainclk = BEETLE_PLL_PRESCALER_36MHZ; } else { pre_mainclk = BEETLE_PLL_PRESCALER_48MHZ; } return pre_mainclk; } static int beetle_pll_enable(uint32_t mainclk) { uint32_t pre_mainclk = beetle_get_prescaler(mainclk); /* Set PLLCTRL Register */ __BEETLE_SYSCON->pllctrl = BEETLE_PLL_CONFIGURATION; /* Switch the the Main clock to PLL and set prescaler */ __BEETLE_SYSCON->mainclk = pre_mainclk; while (!__BEETLE_SYSCON->pllstatus) { /* Wait for PLL to lock */ } return 0; } #endif /* CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL */ static int beetle_clock_control_init(const struct device *dev) { #ifdef CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL const struct beetle_clock_control_cfg_t * const cfg = dev->config; /* * Enable PLL if Beetle is configured to run at a different * frequency than 24Mhz. */ if (cfg->freq != MAINCLK_BASE_FREQ) { beetle_pll_enable(cfg->freq); } #endif /* CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL */ return 0; } static const struct beetle_clock_control_cfg_t beetle_cc_cfg = { .clock_control_id = 0, .freq = DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency), }; /** * @brief Clock Control device init * */ DEVICE_DT_INST_DEFINE(0, &beetle_clock_control_init, NULL, NULL, &beetle_cc_cfg, PRE_KERNEL_1, CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &beetle_clock_control_api);