Add code for sama7g5 Generic Clock, Main Clock, Main System Bus Clock, Peripheral Clock, Programmable Clock and PLL Clock. Signed-off-by: Tony Han <tony.han@microchip.com>
357 lines
7.6 KiB
C
357 lines
7.6 KiB
C
/*
|
|
* Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <pmc.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(clk_main, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
|
|
|
|
#define SLOW_CLOCK_FREQ 32768
|
|
#define MAINF_DIV 16
|
|
|
|
#define clk_main_parent_select(s) (((s) & \
|
|
(CKGR_MOR_MOSCXTEN_Msk | \
|
|
AT91_PMC_OSCBYPASS)) ? 1 : 0)
|
|
|
|
struct clk_main_osc {
|
|
struct device clk;
|
|
|
|
pmc_registers_t *pmc;
|
|
uint32_t frequency;
|
|
};
|
|
|
|
#define to_clk_main_osc(ptr) CONTAINER_OF(ptr, struct clk_main_osc, clk)
|
|
|
|
struct clk_main_rc_osc {
|
|
struct device clk;
|
|
|
|
pmc_registers_t *pmc;
|
|
uint32_t frequency;
|
|
};
|
|
|
|
#define to_clk_main_rc_osc(ptr) CONTAINER_OF(ptr, struct clk_main_rc_osc, clk)
|
|
|
|
struct clk_main {
|
|
struct device clk;
|
|
|
|
pmc_registers_t *pmc;
|
|
struct device *parents[2];
|
|
uint8_t num_parents;
|
|
uint8_t parent;
|
|
};
|
|
|
|
#define to_clk_main(ptr) CONTAINER_OF(ptr, struct clk_main, clk)
|
|
|
|
static struct clk_main_osc main_osc;
|
|
static struct clk_main_rc_osc main_rc_osc;
|
|
static struct clk_main clocks_main;
|
|
|
|
static int main_osc_on(const struct device *dev, clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_osc *osc = to_clk_main_osc(dev);
|
|
pmc_registers_t *pmc = osc->pmc;
|
|
uint32_t tmp, status;
|
|
|
|
tmp = pmc->CKGR_MOR & ~CKGR_MOR_KEY_Msk;
|
|
|
|
if (tmp & AT91_PMC_OSCBYPASS) {
|
|
return 0;
|
|
}
|
|
|
|
if (!(tmp & CKGR_MOR_MOSCXTEN_Msk)) {
|
|
tmp |= CKGR_MOR_MOSCXTEN_Msk | CKGR_MOR_KEY_PASSWD;
|
|
pmc->CKGR_MOR = tmp;
|
|
}
|
|
|
|
do {
|
|
status = pmc->PMC_SR;
|
|
} while (!(status & CKGR_MOR_MOSCXTEN_Msk));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int main_osc_off(const struct device *dev, clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_osc *osc = to_clk_main_osc(dev);
|
|
pmc_registers_t *pmc = osc->pmc;
|
|
uint32_t tmp;
|
|
|
|
tmp = pmc->CKGR_MOR;
|
|
if (!(tmp & AT91_PMC_OSCBYPASS) && (tmp & CKGR_MOR_MOSCXTEN_Msk)) {
|
|
tmp &= ~(CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN_Msk);
|
|
pmc->CKGR_MOR = tmp | CKGR_MOR_KEY_PASSWD;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int main_osc_get_rate(const struct device *dev, clock_control_subsys_t sys, uint32_t *rate)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_osc *osc = to_clk_main_osc(dev);
|
|
|
|
*rate = osc->frequency;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static enum clock_control_status main_osc_get_status(const struct device *dev,
|
|
clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_osc *osc = to_clk_main_osc(dev);
|
|
pmc_registers_t *pmc = osc->pmc;
|
|
uint32_t tmp, status;
|
|
|
|
tmp = pmc->CKGR_MOR;
|
|
if (tmp & AT91_PMC_OSCBYPASS) {
|
|
return CLOCK_CONTROL_STATUS_ON;
|
|
}
|
|
|
|
status = pmc->PMC_SR;
|
|
if ((status & PMC_SR_MOSCXTS_Msk) && clk_main_parent_select(tmp)) {
|
|
return CLOCK_CONTROL_STATUS_ON;
|
|
} else {
|
|
return CLOCK_CONTROL_STATUS_OFF;
|
|
}
|
|
}
|
|
|
|
static DEVICE_API(clock_control, main_osc_api) = {
|
|
.on = main_osc_on,
|
|
.off = main_osc_off,
|
|
.get_rate = main_osc_get_rate,
|
|
.get_status = main_osc_get_status,
|
|
};
|
|
|
|
int clk_register_main_osc(pmc_registers_t *const pmc, const char *name, uint32_t bypass,
|
|
uint32_t frequency, struct device **clk)
|
|
{
|
|
struct clk_main_osc *osc;
|
|
|
|
if (!name) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
osc = &main_osc;
|
|
|
|
*clk = &osc->clk;
|
|
(*clk)->name = name;
|
|
(*clk)->api = &main_osc_api;
|
|
osc->pmc = pmc;
|
|
osc->frequency = frequency;
|
|
|
|
if (bypass) {
|
|
reg_update_bits(pmc->CKGR_MOR, CKGR_MOR_KEY_Msk | AT91_PMC_OSCBYPASS,
|
|
AT91_PMC_OSCBYPASS | CKGR_MOR_KEY_PASSWD);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int main_rc_osc_on(const struct device *dev, clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(dev);
|
|
pmc_registers_t *pmc = osc->pmc;
|
|
uint32_t mor, status;
|
|
|
|
mor = pmc->CKGR_MOR;
|
|
|
|
if (!(mor & CKGR_MOR_MOSCRCEN_Msk)) {
|
|
reg_update_bits(pmc->CKGR_MOR,
|
|
CKGR_MOR_KEY_Msk | CKGR_MOR_MOSCRCEN_Msk,
|
|
CKGR_MOR_MOSCRCEN_Msk | CKGR_MOR_KEY_PASSWD);
|
|
}
|
|
|
|
do {
|
|
status = pmc->PMC_SR;
|
|
} while (!(status & PMC_SR_MOSCRCS_Msk));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int main_rc_osc_off(const struct device *dev, clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(dev);
|
|
pmc_registers_t *pmc = osc->pmc;
|
|
|
|
if (pmc->CKGR_MOR & CKGR_MOR_MOSCRCEN_Msk) {
|
|
reg_update_bits(pmc->CKGR_MOR,
|
|
CKGR_MOR_KEY_Msk | CKGR_MOR_MOSCRCEN_Msk, CKGR_MOR_KEY_PASSWD);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int main_rc_osc_get_rate(const struct device *dev,
|
|
clock_control_subsys_t sys, uint32_t *rate)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(dev);
|
|
|
|
*rate = osc->frequency;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static enum clock_control_status main_rc_osc_get_status(const struct device *dev,
|
|
clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main_rc_osc *osc = to_clk_main_rc_osc(dev);
|
|
pmc_registers_t *pmc = osc->pmc;
|
|
|
|
if ((pmc->CKGR_MOR & CKGR_MOR_MOSCRCEN_Msk) && (pmc->PMC_SR & PMC_SR_MOSCRCS_Msk)) {
|
|
return CLOCK_CONTROL_STATUS_ON;
|
|
} else {
|
|
return CLOCK_CONTROL_STATUS_OFF;
|
|
}
|
|
}
|
|
|
|
static DEVICE_API(clock_control, main_rc_osc_api) = {
|
|
.on = main_rc_osc_on,
|
|
.off = main_rc_osc_off,
|
|
.get_rate = main_rc_osc_get_rate,
|
|
.get_status = main_rc_osc_get_status,
|
|
};
|
|
|
|
int clk_register_main_rc_osc(pmc_registers_t *const pmc, const char *name,
|
|
uint32_t frequency, struct device **clk)
|
|
{
|
|
struct clk_main_rc_osc *osc;
|
|
|
|
if (!name || !frequency) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
osc = &main_rc_osc;
|
|
|
|
*clk = &osc->clk;
|
|
(*clk)->api = &main_rc_osc_api;
|
|
(*clk)->name = name;
|
|
osc->pmc = pmc;
|
|
osc->frequency = frequency;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int clk_main_probe_frequency(pmc_registers_t *pmc)
|
|
{
|
|
uint32_t timeout = 0;
|
|
|
|
do {
|
|
if (pmc->CKGR_MCFR & CKGR_MCFR_MAINFRDY_Msk) {
|
|
return 0;
|
|
}
|
|
k_busy_wait(10);
|
|
} while (timeout++ < 10);
|
|
LOG_WRN("Wait Main Clock Frequency Measure Ready timeout.");
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
static inline bool clk_main_ready(pmc_registers_t *pmc)
|
|
{
|
|
return !!(pmc->PMC_SR & PMC_SR_MOSCSELS_Msk);
|
|
}
|
|
|
|
static int clk_main_on(const struct device *dev, clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main *clkmain = to_clk_main(dev);
|
|
pmc_registers_t *pmc = clkmain->pmc;
|
|
|
|
while (!clk_main_ready(pmc)) {
|
|
k_busy_wait(1);
|
|
}
|
|
|
|
return clk_main_probe_frequency(pmc);
|
|
}
|
|
|
|
static int clk_main_get_rate(const struct device *dev, clock_control_subsys_t sys, uint32_t *rate)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main *clkmain = to_clk_main(dev);
|
|
pmc_registers_t *pmc = clkmain->pmc;
|
|
uint32_t mcfr;
|
|
|
|
if (clkmain->parent == 1) {
|
|
return clock_control_get_rate(clkmain->parents[1], NULL, rate);
|
|
}
|
|
|
|
mcfr = pmc->CKGR_MCFR;
|
|
if (!(mcfr & CKGR_MCFR_MAINFRDY_Msk)) {
|
|
*rate = 0;
|
|
} else {
|
|
*rate = ((mcfr & CKGR_MCFR_MAINF_Msk) * SLOW_CLOCK_FREQ) / MAINF_DIV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static enum clock_control_status clk_main_get_status(const struct device *dev,
|
|
clock_control_subsys_t sys)
|
|
{
|
|
ARG_UNUSED(sys);
|
|
|
|
struct clk_main *clkmain = to_clk_main(dev);
|
|
|
|
if (clk_main_ready(clkmain->pmc)) {
|
|
return CLOCK_CONTROL_STATUS_ON;
|
|
} else {
|
|
return CLOCK_CONTROL_STATUS_OFF;
|
|
}
|
|
}
|
|
|
|
static DEVICE_API(clock_control, main_api) = {
|
|
.on = clk_main_on,
|
|
.get_rate = clk_main_get_rate,
|
|
.get_status = clk_main_get_status,
|
|
};
|
|
|
|
int clk_register_main(pmc_registers_t *const pmc, const char *name,
|
|
const struct device **parents, int num_parents, struct device **clk)
|
|
{
|
|
struct clk_main *clkmain;
|
|
unsigned int status;
|
|
|
|
if (!name || !parents || !num_parents) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
clkmain = &clocks_main;
|
|
if (num_parents > ARRAY_SIZE(clkmain->parents)) {
|
|
LOG_ERR("Array for parent clock not enough");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(clkmain->parents, parents, sizeof(struct device *) * num_parents);
|
|
|
|
*clk = &clkmain->clk;
|
|
(*clk)->name = name;
|
|
(*clk)->api = &main_api;
|
|
clkmain->num_parents = num_parents;
|
|
clkmain->pmc = pmc;
|
|
status = pmc->CKGR_MOR;
|
|
clkmain->parent = clk_main_parent_select(status);
|
|
|
|
return 0;
|
|
}
|