259 lines
6.3 KiB
C
259 lines
6.3 KiB
C
/*
|
|
* Copyright (c) 2016 Linaro Limited.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* @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 */
|
|
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(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 = 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);
|
|
|
|
uint8_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,
|
|
uint32_t *rate)
|
|
{
|
|
#ifdef CONFIG_CLOCK_CONTROL_BEETLE_ENABLE_PLL
|
|
const struct beetle_clock_control_cfg_t * const cfg =
|
|
clock->config->config_info;
|
|
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 = 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 uint32_t beetle_get_prescaler(uint32_t mainclk)
|
|
{
|
|
uint32_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(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(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);
|