blob: 33f202cbb2b35466566c8f8c687ce3e4666753e3 [file] [log] [blame]
/*
* 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 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);