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.
249 lines
5.8 KiB
249 lines
5.8 KiB
/* |
|
* 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 <soc.h> |
|
#include <zephyr/drivers/clock_control.h> |
|
#include <zephyr/irq.h> |
|
#include <zephyr/sys/util.h> |
|
#include <zephyr/drivers/clock_control/arm_clock_control.h> |
|
|
|
#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);
|
|
|