| /* |
| * Copyright (c) 2016 Linaro Limited. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| /** |
| * @file |
| * @brief Driver for Clock Control of Beetle MCUs. |
| * |
| * This file contains the Clock Control driver implementation for the |
| * Beetle MCUs. |
| */ |
| |
| #include <soc.h> |
| #include <clock_control.h> |
| #include <misc/util.h> |
| #include <clock_control/arm_clock_control.h> |
| |
| #define MAINCLK_BASE_FREQ 24000000 |
| |
| struct beetle_clock_control_cfg_t { |
| /* Clock Control ID */ |
| u32_t clock_control_id; |
| /* Clock control freq */ |
| u32_t freq; |
| }; |
| |
| static inline void beetle_set_clock(volatile u32_t *base, |
| u8_t bit, enum arm_soc_state_t state) |
| { |
| u32_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(u8_t bit, |
| enum arm_soc_state_t state) |
| { |
| beetle_set_clock((volatile u32_t *)&(__BEETLE_SYSCON->ahbclkcfg0set), |
| bit, state); |
| } |
| |
| static inline void beetle_ahb_set_clock_off(u8_t bit, |
| enum arm_soc_state_t state) |
| { |
| beetle_set_clock((volatile u32_t *)&(__BEETLE_SYSCON->ahbclkcfg0clr), |
| bit, state); |
| } |
| |
| static inline void beetle_apb_set_clock_on(u8_t bit, |
| enum arm_soc_state_t state) |
| { |
| beetle_set_clock((volatile u32_t *)&(__BEETLE_SYSCON->apbclkcfg0set), |
| bit, state); |
| } |
| |
| static inline void beetle_apb_set_clock_off(u8_t bit, |
| enum arm_soc_state_t state) |
| { |
| beetle_set_clock((volatile u32_t *)&(__BEETLE_SYSCON->apbclkcfg0clr), |
| bit, state); |
| } |
| |
| static inline int beetle_clock_control_on(struct device *dev, |
| clock_control_subsys_t sub_system) |
| { |
| struct arm_clock_control_t *beetle_cc = |
| (struct arm_clock_control_t *)(sub_system); |
| |
| u8_t bit = 0; |
| |
| 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(struct device *dev, |
| clock_control_subsys_t sub_system) |
| { |
| struct arm_clock_control_t *beetle_cc = |
| (struct arm_clock_control_t *)(sub_system); |
| |
| u8_t bit = 0; |
| |
| 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(struct device *clock, |
| clock_control_subsys_t sub_system, |
| u32_t *rate) |
| { |
| #ifdef CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL |
| const struct beetle_clock_control_cfg_t * const cfg = |
| clock->config->config_info; |
| u32_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 u32_t beetle_round_freq(u32_t mainclk) |
| { |
| u32_t nc_mainclk = 0; |
| |
| /* |
| * 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 u32_t beetle_get_prescaler(u32_t mainclk) |
| { |
| u32_t pre_mainclk = 0; |
| |
| /* |
| * 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(u32_t mainclk) |
| { |
| |
| u32_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(struct device *dev) |
| { |
| #ifdef CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL |
| const struct beetle_clock_control_cfg_t * const cfg = |
| dev->config->config_info; |
| |
| /* |
| * 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 = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC, |
| }; |
| |
| /** |
| * @brief Clock Control device init |
| * |
| */ |
| DEVICE_AND_API_INIT(clock_control_beetle, CONFIG_ARM_CLOCK_CONTROL_DEV_NAME, |
| &beetle_clock_control_init, |
| NULL, &beetle_cc_cfg, |
| PRE_KERNEL_1, |
| CONFIG_CLOCK_CONTROL_BEETLE_DEVICE_INIT_PRIORITY, |
| &beetle_clock_control_api); |