soc: microchip: sam: add code for sama7g5 clocks

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>
This commit is contained in:
Tony Han 2025-04-24 16:39:14 +08:00 committed by Benjamin Cabé
commit 21da37b400
13 changed files with 3513 additions and 0 deletions

View file

@ -0,0 +1,17 @@
# Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
#
# SPDX-License-Identifier: Apache-2.0
#
zephyr_include_directories(.)
zephyr_sources(clk-generated.c)
zephyr_sources(clk-main.c)
zephyr_sources(clk-master.c)
zephyr_sources(clk-peripheral.c)
zephyr_sources(clk-programmable.c)
zephyr_sources(clk-sam9x60-pll.c)
zephyr_sources(clk-system.c)
zephyr_sources(clk-utmi.c)
zephyr_sources(pmc.c)
zephyr_sources_ifdef(CONFIG_SOC_SERIES_SAMA7G5 sama7g5.c)

View file

@ -0,0 +1,191 @@
/*
* 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/spinlock.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clk_generated, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
struct clk_generated {
struct device clk;
struct device *parents[8];
pmc_registers_t *pmc;
struct clk_range range;
struct k_spinlock *lock;
uint32_t mux_table[8];
uint32_t id;
uint32_t gckdiv;
const struct clk_pcr_layout *layout;
uint8_t num_parents;
uint8_t parent_id;
int chg_pid;
};
#define to_clk_generated(ptr) CONTAINER_OF(ptr, struct clk_generated, clk)
static struct clk_generated clocks_gck[ID_PERIPH_MAX];
static uint32_t clocks_gck_idx;
static int clk_generated_on(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_generated *gck = to_clk_generated(dev);
uint32_t mask = gck->layout->cmd | PMC_PCR_GCLKEN_Msk;
uint32_t val = gck->layout->cmd | PMC_PCR_GCLKEN_Msk;
k_spinlock_key_t key;
LOG_DBG("gckdiv = %d, parent id = %d\n", gck->gckdiv, gck->parent_id);
mask |= PMC_PCR_GCLKDIV_Msk | gck->layout->gckcss_mask;
val |= FIELD_PREP(PMC_PCR_GCLKDIV_Msk, gck->gckdiv);
val |= FIELD_PREP(gck->layout->gckcss_mask, gck->parent_id);
key = k_spin_lock(gck->lock);
regmap_write((void *)gck->pmc, gck->layout->offset, (gck->id & gck->layout->pid_mask));
regmap_update_bits((void *)gck->pmc, gck->layout->offset, mask, val);
k_spin_unlock(gck->lock, key);
return 0;
}
static int clk_generated_off(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_generated *gck = to_clk_generated(dev);
k_spinlock_key_t key = k_spin_lock(gck->lock);
regmap_write((void *)gck->pmc, gck->layout->offset, (gck->id & gck->layout->pid_mask));
regmap_update_bits((void *)gck->pmc, gck->layout->offset,
gck->layout->cmd | PMC_PCR_GCLKEN_Msk,
gck->layout->cmd);
k_spin_unlock(gck->lock, key);
return 0;
}
static enum clock_control_status clk_generated_get_status(const struct device *dev,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_generated *gck = to_clk_generated(dev);
k_spinlock_key_t key = k_spin_lock(gck->lock);
uint32_t status;
regmap_write((void *)gck->pmc, gck->layout->offset, (gck->id & gck->layout->pid_mask));
regmap_read((void *)gck->pmc, gck->layout->offset, &status);
k_spin_unlock(gck->lock, key);
if (!!(status & PMC_PCR_GCLKEN_Msk)) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
}
static int clk_generated_get_rate(const struct device *dev,
clock_control_subsys_t sys, uint32_t *rate)
{
ARG_UNUSED(sys);
struct clk_generated *gck = to_clk_generated(dev);
const struct device *parent = NULL;
uint32_t status, css, i = 0;
k_spinlock_key_t key;
int ret;
if (clk_generated_get_status(dev, sys) == CLOCK_CONTROL_STATUS_OFF) {
*rate = 0;
return 0;
}
key = k_spin_lock(gck->lock);
regmap_write((void *)gck->pmc, gck->layout->offset, (gck->id & gck->layout->pid_mask));
regmap_read((void *)gck->pmc, gck->layout->offset, &status);
k_spin_unlock(gck->lock, key);
while (!(gck->layout->gckcss_mask & (1 << i))) {
i++;
}
css = (status & gck->layout->gckcss_mask) >> i;
for (i = 0; i < gck->num_parents; i++) {
if (css == gck->mux_table[i]) {
parent = gck->parents[i];
break;
}
}
if (!parent) {
LOG_ERR("find parent clock failed.");
return -ENXIO;
}
ret = clock_control_get_rate(parent, NULL, rate);
if (ret) {
LOG_ERR("get parent clock rate failed.");
return ret;
}
*rate /= (gck->gckdiv + 1);
return 0;
}
static DEVICE_API(clock_control, generated_api) = {
.on = clk_generated_on,
.off = clk_generated_off,
.get_rate = clk_generated_get_rate,
.get_status = clk_generated_get_status,
};
int clk_register_generated(pmc_registers_t *const pmc, struct k_spinlock *lock,
const struct clk_pcr_layout *layout,
const char *name,
const struct device **parents, uint32_t *mux_table,
uint8_t num_parents, uint8_t id,
const struct clk_range *range, int chg_pid, struct device **clk)
{
struct clk_generated *gck;
if (!parents) {
return -EINVAL;
}
gck = &clocks_gck[clocks_gck_idx++];
if (clocks_gck_idx > ARRAY_SIZE(clocks_gck)) {
LOG_ERR("Array for generated clock not enough");
return -ENOMEM;
}
if (num_parents > ARRAY_SIZE(gck->parents)) {
LOG_ERR("Array for parent clock not enough");
return -ENOMEM;
}
if (num_parents > ARRAY_SIZE(gck->mux_table)) {
LOG_ERR("Array for mux table not enough");
return -ENOMEM;
}
memcpy(gck->parents, parents, sizeof(struct device *) * num_parents);
*clk = &gck->clk;
(*clk)->name = name;
(*clk)->api = &generated_api;
gck->num_parents = num_parents;
gck->id = id;
gck->pmc = pmc;
gck->lock = lock;
gck->range = *range;
gck->chg_pid = chg_pid;
gck->layout = layout;
memcpy(gck->mux_table, mux_table, num_parents * sizeof(mux_table));
return 0;
}

View file

@ -0,0 +1,356 @@
/*
* 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;
}

View file

@ -0,0 +1,320 @@
/*
* 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/spinlock.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clk_mck, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
#define MASTER_PRES_MASK 0x7
#define MASTER_PRES_MAX MASTER_PRES_MASK
#define MASTER_DIV_SHIFT 8
#define MASTER_DIV_MASK 0x7
#define PMC_MCR_CSS_SHIFT 16
#define MASTER_MAX_ID 4
#define to_clk_master(ptr) CONTAINER_OF(ptr, struct clk_master, clk)
struct clk_master {
pmc_registers_t *pmc;
struct device clk;
struct device *parents[6];
struct k_spinlock *lock;
const struct clk_master_layout *layout;
const struct clk_master_characteristics *characteristics;
uint32_t mux_table[6];
uint32_t mckr;
uint8_t id;
uint8_t num_parents;
uint8_t parent;
uint8_t div;
};
static struct clk_master clocks_master[5];
static uint32_t clocks_master_idx;
static inline bool clk_master_ready(struct clk_master *master)
{
uint32_t bit = master->id ? PMC_SR_MCKXRDY_Msk : PMC_SR_MCKRDY_Msk;
uint32_t status;
status = master->pmc->PMC_SR;
return !!(status & bit);
}
static int clk_master_div_on(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_master *master = to_clk_master(dev);
k_spinlock_key_t key;
key = k_spin_lock(master->lock);
while (!clk_master_ready(master)) {
k_busy_wait(1);
}
k_spin_unlock(master->lock, key);
return 0;
}
static enum clock_control_status clk_master_div_get_status(const struct device *dev,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_master *master = to_clk_master(dev);
k_spinlock_key_t key;
bool status;
key = k_spin_lock(master->lock);
status = clk_master_ready(master);
k_spin_unlock(master->lock, key);
return status ? CLOCK_CONTROL_STATUS_ON : CLOCK_CONTROL_STATUS_OFF;
}
static int clk_master_div_get_rate(const struct device *dev,
clock_control_subsys_t sys, uint32_t *rate)
{
ARG_UNUSED(sys);
struct clk_master *master = to_clk_master(dev);
uint32_t retval = 0;
retval = clock_control_get_rate(master->parents[master->parent], NULL, rate);
if (!retval) {
*rate /= master->div;
}
LOG_DBG("%s, rate %d", dev->name, *rate);
return retval;
}
static DEVICE_API(clock_control, master_div_api) = {
.on = clk_master_div_on,
.get_rate = clk_master_div_get_rate,
.get_status = clk_master_div_get_status,
};
int clk_register_master_div(pmc_registers_t *const pmc, const char *name,
const struct device *parent, const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
struct k_spinlock *lock, uint32_t safe_div, struct device **clk)
{
struct clk_master *master;
k_spinlock_key_t key;
uint32_t mckr;
if (!name || !lock) {
return -EINVAL;
}
master = &clocks_master[clocks_master_idx++];
if (clocks_master_idx > ARRAY_SIZE(clocks_master)) {
LOG_ERR("Array for master clock not enough");
return -ENOMEM;
}
*clk = &master->clk;
(*clk)->name = name;
(*clk)->api = &master_div_api;
memcpy(master->parents, &parent, sizeof(struct device *) * 1);
master->num_parents = 1;
master->parent = 0;
master->layout = layout;
master->characteristics = characteristics;
master->pmc = pmc;
master->lock = lock;
key = k_spin_lock(master->lock);
regmap_read((void *)master->pmc, master->layout->offset, &mckr);
k_spin_unlock(master->lock, key);
mckr &= layout->mask;
mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
master->div = characteristics->divisors[mckr];
return 0;
}
static int clk_mck_get_rate(const struct device *dev, clock_control_subsys_t sys, uint32_t *rate)
{
ARG_UNUSED(sys);
struct clk_master *master = to_clk_master(dev);
int retval = 0;
const struct device *parent;
uint8_t i;
for (i = 0; ; i++) {
if (i >= master->num_parents) {
LOG_ERR("clk %s: source %d not found", dev->name, i);
return ESRCH;
}
if (master->mux_table[i] == master->parent) {
parent = master->parents[i];
break;
}
}
retval = clock_control_get_rate(parent, NULL, rate);
if (retval) {
LOG_ERR("get parent clock rate failed.");
} else {
if (master->div == MASTER_PRES_MAX) {
*rate /= 3;
} else {
*rate /= 1 << master->div;
}
}
LOG_DBG("clk %s: rate %d", dev->name, *rate);
return retval;
}
static int clk_mck_on(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_master *master = to_clk_master(dev);
k_spinlock_key_t key;
uint32_t val, cparent;
uint32_t enable = PMC_MCR_EN_Msk;
uint32_t parent = master->parent << PMC_MCR_CSS_SHIFT;
uint32_t div = master->div << MASTER_DIV_SHIFT;
key = k_spin_lock(master->lock);
master->pmc->PMC_MCR = PMC_MCR_ID(master->id);
val = master->pmc->PMC_MCR;
reg_update_bits(master->pmc->PMC_MCR,
enable | PMC_MCR_CSS_Msk | PMC_MCR_DIV_Msk |
PMC_MCR_CMD_Msk | PMC_MCR_ID_Msk,
enable | parent | div | PMC_MCR_CMD_Msk |
PMC_MCR_ID(master->id));
cparent = (val & PMC_MCR_CSS_Msk) >> PMC_MCR_CSS_SHIFT;
/* Wait here only if parent is being changed. */
while ((cparent != master->parent) && !clk_master_ready(master)) {
k_busy_wait(1);
}
k_spin_unlock(master->lock, key);
return 0;
}
static int clk_mck_off(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_master *master = to_clk_master(dev);
k_spinlock_key_t key;
key = k_spin_lock(master->lock);
master->pmc->PMC_MCR = master->id;
reg_update_bits(master->pmc->PMC_MCR,
PMC_MCR_EN_Msk | PMC_MCR_CMD_Msk |
PMC_MCR_ID_Msk,
PMC_MCR_CMD_Msk |
PMC_MCR_ID(master->id));
k_spin_unlock(master->lock, key);
return 0;
}
static enum clock_control_status clk_mck_get_status(const struct device *dev,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_master *master = to_clk_master(dev);
k_spinlock_key_t key;
uint32_t val;
key = k_spin_lock(master->lock);
master->pmc->PMC_MCR = master->id;
val = master->pmc->PMC_MCR;
k_spin_unlock(master->lock, key);
if (!!(val & PMC_MCR_EN_Msk)) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
}
static DEVICE_API(clock_control, sama7g5_master_api) = {
.on = clk_mck_on,
.off = clk_mck_off,
.get_rate = clk_mck_get_rate,
.get_status = clk_mck_get_status,
};
int clk_register_master(pmc_registers_t *const pmc,
const char *name, int num_parents,
const struct device **parents,
uint32_t *mux_table,
struct k_spinlock *lock, uint8_t id,
struct device **clk)
{
struct clk_master *master;
k_spinlock_key_t key;
unsigned int val;
if (!num_parents || !parents || !mux_table || !lock || id > MASTER_MAX_ID) {
return -EINVAL;
}
master = &clocks_master[clocks_master_idx++];
if (clocks_master_idx > ARRAY_SIZE(clocks_master)) {
LOG_ERR("Array for master clock not enough");
return -ENOMEM;
}
if (num_parents > ARRAY_SIZE(master->parents)) {
LOG_ERR("Array for parent clock not enough");
return -ENOMEM;
}
if (num_parents > ARRAY_SIZE(master->mux_table)) {
LOG_ERR("Array for mux table not enough");
return -ENOMEM;
}
memcpy(master->parents, parents, sizeof(struct device *) * num_parents);
*clk = &master->clk;
(*clk)->name = name;
(*clk)->api = &sama7g5_master_api;
master->num_parents = num_parents;
master->pmc = pmc;
master->id = id;
master->lock = lock;
memcpy(master->mux_table, mux_table, num_parents * sizeof(mux_table));
key = k_spin_lock(master->lock);
pmc->PMC_MCR = master->id;
val = pmc->PMC_MCR;
master->parent = (val & PMC_MCR_CSS_Msk) >> PMC_MCR_CSS_SHIFT;
master->div = (val & PMC_MCR_DIV_Msk) >> MASTER_DIV_SHIFT;
k_spin_unlock(master->lock, key);
return 0;
}

View file

@ -0,0 +1,159 @@
/*
* 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/spinlock.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clk_peripheral, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
#define PERIPHERAL_ID_MIN 2
struct clk_peripheral {
pmc_registers_t *pmc;
struct device clk;
struct device *parent;
struct clk_range range;
struct k_spinlock *lock;
uint32_t id;
const struct clk_pcr_layout *layout;
};
static struct clk_peripheral clocks_periph[72];
static uint32_t clocks_periph_idx;
#define to_clk_peripheral(ptr) CONTAINER_OF(ptr, struct clk_peripheral, clk)
static void clk_peripheral_set(struct clk_peripheral *periph,
uint32_t enable)
{
k_spinlock_key_t key = k_spin_lock(periph->lock);
LOG_DBG("id %d, enable %d", periph->id, enable);
enable = enable ? PMC_PCR_EN_Msk : 0;
regmap_write((void *)periph->pmc, periph->layout->offset,
periph->id & periph->layout->pid_mask);
regmap_update_bits((void *)periph->pmc, periph->layout->offset,
periph->layout->cmd | enable,
periph->layout->cmd | enable);
k_spin_unlock(periph->lock, key);
}
static int clk_peripheral_on(const struct device *clk,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
clk_peripheral_set(to_clk_peripheral(clk), 1);
return 0;
}
static int clk_peripheral_off(const struct device *clk,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
clk_peripheral_set(to_clk_peripheral(clk), 0);
return 0;
}
static enum clock_control_status clk_peripheral_get_status(const struct device *dev,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_peripheral *periph = to_clk_peripheral(dev);
k_spinlock_key_t key;
uint32_t status;
if (periph->id < PERIPHERAL_ID_MIN) {
return CLOCK_CONTROL_STATUS_UNKNOWN;
}
key = k_spin_lock(periph->lock);
regmap_write((void *)periph->pmc, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
regmap_read((void *)periph->pmc, periph->layout->offset, &status);
k_spin_unlock(periph->lock, key);
if (!!(status & PMC_PCR_EN_Msk)) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
}
static int clk_peripheral_get_rate(const struct device *clk,
clock_control_subsys_t sys,
uint32_t *rate)
{
ARG_UNUSED(sys);
uint32_t retval = 0;
uint32_t status;
struct clk_peripheral *periph = to_clk_peripheral(clk);
regmap_write((void *)periph->pmc, periph->layout->offset,
(periph->id & periph->layout->pid_mask));
regmap_read((void *)periph->pmc, periph->layout->offset, &status);
if (status & PMC_PCR_EN_Msk) {
retval = clock_control_get_rate(periph->parent, NULL, rate);
} else {
*rate = 0;
}
LOG_DBG("id %d, rate %d", periph->id, *rate);
return retval;
}
static DEVICE_API(clock_control, clk_peripheral_api) = {
.on = clk_peripheral_on,
.off = clk_peripheral_off,
.get_rate = clk_peripheral_get_rate,
.get_status = clk_peripheral_get_status,
};
int clk_register_peripheral(pmc_registers_t *const pmc,
struct k_spinlock *lock,
const struct clk_pcr_layout *layout,
const char *name,
struct device *parent,
uint32_t id,
const struct clk_range *range,
struct device **clk)
{
struct clk_peripheral *periph;
if (!name) {
return -EINVAL;
}
periph = &clocks_periph[clocks_periph_idx++];
if (clocks_periph_idx > ARRAY_SIZE(clocks_periph)) {
LOG_ERR("Array for peripheral clock not enough");
return -ENOMEM;
}
*clk = &periph->clk;
periph->clk.name = name;
periph->clk.api = &clk_peripheral_api;
periph->parent = parent;
periph->id = id;
periph->pmc = pmc;
periph->lock = lock;
periph->layout = layout;
periph->range = *range;
return 0;
}

View file

@ -0,0 +1,116 @@
/*
* 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/spinlock.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clk_prog, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
#define PROG_ID_MAX 7
#define PROG_PRES(layout, pckr) ((pckr >> layout->pres_shift) & layout->pres_mask)
struct clk_programmable {
struct device clk;
struct device *parents[9];
pmc_registers_t *pmc;
uint32_t *mux_table;
uint8_t id;
uint8_t num_parents;
uint8_t parent;
const struct clk_programmable_layout *layout;
};
#define to_clk_programmable(ptr) CONTAINER_OF(ptr, struct clk_programmable, clk)
static struct clk_programmable clocks_prog[8];
static uint32_t clocks_prog_idx;
static int clk_programmable_get_rate(const struct device *dev,
clock_control_subsys_t sys, uint32_t *rate)
{
ARG_UNUSED(sys);
struct clk_programmable *prog = to_clk_programmable(dev);
const struct clk_programmable_layout *layout = prog->layout;
const struct device *parent = NULL;
uint32_t pckr;
uint32_t css;
uint32_t i;
int ret;
pckr = prog->pmc->PMC_PCK[prog->id];
css = pckr & PMC_PCK_CSS_Msk;
for (i = 0; i < prog->num_parents; i++) {
if (css == prog->mux_table[i]) {
parent = prog->parents[i];
break;
}
}
if (!parent) {
LOG_ERR("find parent clock failed.");
return -ENXIO;
}
ret = clock_control_get_rate(parent, NULL, rate);
if (ret) {
LOG_ERR("get parent clock rate failed.");
return ret;
}
if (layout->is_pres_direct) {
*rate = *rate / (PROG_PRES(layout, pckr) + 1);
} else {
*rate = *rate >> PROG_PRES(layout, pckr);
}
return 0;
}
static DEVICE_API(clock_control, programmable_api) = {
.get_rate = clk_programmable_get_rate,
};
int clk_register_programmable(pmc_registers_t *const pmc, const char *name,
const struct device **parents,
uint8_t num_parents, uint8_t id,
const struct clk_programmable_layout *layout,
uint32_t *mux_table, struct device **clk)
{
struct clk_programmable *prog;
if (id > PROG_ID_MAX || !parents) {
return -EINVAL;
}
prog = &clocks_prog[clocks_prog_idx++];
if (clocks_prog_idx > ARRAY_SIZE(clocks_prog)) {
LOG_ERR("Array for programmable clock not enough");
return -ENOMEM;
}
if (num_parents > ARRAY_SIZE(prog->parents)) {
LOG_ERR("Array for parent clock not enough, at least %d", num_parents);
return -ENOMEM;
}
memcpy(prog->parents, parents, sizeof(struct device *) * num_parents);
*clk = &prog->clk;
(*clk)->name = name;
(*clk)->api = &programmable_api;
prog->num_parents = num_parents;
prog->id = id;
prog->layout = layout;
prog->pmc = pmc;
prog->mux_table = mux_table;
return 0;
}

View file

@ -0,0 +1,525 @@
/*
* 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/spinlock.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clk_pll, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0)
#define PMC_PLL_CTRL1_MUL_MSK GENMASK(31, 24)
#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0)
# define do_div(n, base) ({ \
uint32_t __base = (base); \
uint32_t __rem; \
__rem = ((uint64_t)(n)) % __base; \
(n) = ((uint64_t)(n)) / __base; \
__rem; \
})
#define DIV_ROUND_CLOSEST_ULL(x, divisor)( \
{ \
__typeof__(divisor) __d = divisor; \
unsigned long long _tmp = (x) + (__d) / 2; \
do_div(_tmp, __d); \
_tmp; \
} \
)
/* Calculate "x * n / d" without unnecessary overflow or loss of precision. */
#define mult_frac(x, n, d) \
({ \
__typeof__(x) x_ = (x); \
__typeof__(n) n_ = (n); \
__typeof__(d) d_ = (d); \
\
__typeof__(x_) q = x_ / d_; \
__typeof__(x_) r = x_ % d_; \
q * n_ + r * n_ / d_; \
})
#define PLL_MAX_ID 7
struct sam9x60_pll_core {
pmc_registers_t *pmc;
struct k_spinlock *lock;
const struct clk_pll_characteristics *characteristics;
const struct clk_pll_layout *layout;
struct device clk;
const struct device *parent;
uint8_t id;
};
struct sam9x60_frac {
struct sam9x60_pll_core core;
uint32_t frac;
uint16_t mul;
};
struct sam9x60_div {
struct sam9x60_pll_core core;
uint8_t div;
};
#define to_sam9x60_pll_core(ptr) CONTAINER_OF(ptr, struct sam9x60_pll_core, clk)
#define to_sam9x60_frac(ptr) CONTAINER_OF(ptr, struct sam9x60_frac, core)
#define to_sam9x60_div(ptr) CONTAINER_OF(ptr, struct sam9x60_div, core)
static struct sam9x60_frac clocks_frac[7];
static uint32_t clocks_frac_idx;
static struct sam9x60_div clocks_div[8];
static uint32_t clocks_div_idx;
static inline bool sam9x60_pll_ready(pmc_registers_t *pmc, int id)
{
unsigned int status;
status = pmc->PMC_PLL_ISR0;
return !!(status & BIT(id));
}
static int sam9x60_frac_pll_set(struct sam9x60_pll_core *core)
{
struct sam9x60_frac *frac = to_sam9x60_frac(core);
pmc_registers_t *pmc = core->pmc;
uint32_t val, cfrac, cmul;
k_spinlock_key_t key;
key = k_spin_lock(core->lock);
reg_update_bits(pmc->PMC_PLL_UPDT, PMC_PLL_UPDT_ID_Msk, core->id);
val = pmc->PMC_PLL_CTRL1;
cmul = (val & core->layout->mul_mask) >> core->layout->mul_shift;
cfrac = (val & core->layout->frac_mask) >> core->layout->frac_shift;
if (sam9x60_pll_ready(pmc, core->id) &&
(cmul == frac->mul && cfrac == frac->frac)) {
goto unlock;
}
/* Recommended value for PMC_PLL_ACR */
if (core->characteristics->upll) {
val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
} else {
val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
}
pmc->PMC_PLL_ACR = val;
pmc->PMC_PLL_CTRL1 = (frac->mul << core->layout->mul_shift) |
(frac->frac << core->layout->frac_shift);
#ifdef PMC_PLL_ACR_UTMIBG_Msk
if (core->characteristics->upll) {
/* Enable the UTMI internal bandgap */
val |= PMC_PLL_ACR_UTMIBG_Msk;
pmc->PMC_PLL_ACR = val;
k_busy_wait(10);
/* Enable the UTMI internal regulator */
val |= PMC_PLL_ACR_UTMIVR_Msk;
pmc->PMC_PLL_ACR = val;
k_busy_wait(10);
}
#endif
reg_update_bits(pmc->PMC_PLL_UPDT,
PMC_PLL_UPDT_UPDATE_Msk | PMC_PLL_UPDT_ID_Msk,
PMC_PLL_UPDT_UPDATE_Msk | core->id);
pmc->PMC_PLL_CTRL0 |= PMC_PLL_CTRL0_ENLOCK_Msk | PMC_PLL_CTRL0_ENPLL_Msk;
reg_update_bits(pmc->PMC_PLL_UPDT,
PMC_PLL_UPDT_UPDATE_Msk | PMC_PLL_UPDT_ID_Msk,
PMC_PLL_UPDT_UPDATE_Msk | core->id);
while (!sam9x60_pll_ready(pmc, core->id)) {
k_busy_wait(1);
}
unlock:
k_spin_unlock(core->lock, key);
return 0;
}
static int sam9x60_clk_frac_pll_on(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
return sam9x60_frac_pll_set(core);
}
static int sam9x60_clk_frac_pll_off(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
pmc_registers_t *pmc = core->pmc;
k_spinlock_key_t key;
key = k_spin_lock(core->lock);
reg_update_bits(pmc->PMC_PLL_UPDT, PMC_PLL_UPDT_ID_Msk, core->id);
reg_update_bits(pmc->PMC_PLL_CTRL0, PMC_PLL_CTRL0_ENPLL_Msk, 0);
#ifdef PMC_PLL_ACR_UTMIBG_Msk
if (core->characteristics->upll) {
reg_update_bits(pmc->PMC_PLL_ACR,
PMC_PLL_ACR_UTMIBG_Msk | PMC_PLL_ACR_UTMIVR_Msk, 0);
}
#endif
reg_update_bits(pmc->PMC_PLL_UPDT,
PMC_PLL_UPDT_UPDATE_Msk | PMC_PLL_UPDT_ID_Msk,
PMC_PLL_UPDT_UPDATE_Msk | core->id);
k_spin_unlock(core->lock, key);
return 0;
}
static enum clock_control_status sam9x60_clk_frac_pll_get_status(const struct device *dev,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
if (sam9x60_pll_ready(core->pmc, core->id)) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
}
static long sam9x60_frac_pll_compute_mul_frac(struct sam9x60_pll_core *core,
unsigned long rate,
unsigned long parent_rate,
bool update)
{
struct sam9x60_frac *frac = to_sam9x60_frac(core);
unsigned long tmprate, remainder;
unsigned long nmul = 0;
unsigned long nfrac = 0;
if (rate < core->characteristics->core_output[0].min ||
rate > core->characteristics->core_output[0].max) {
return -ERANGE;
}
/*
* Calculate the multiplier associated with the current
* divider that provide the closest rate to the requested one.
*/
nmul = mult_frac(rate, 1, parent_rate);
tmprate = mult_frac(parent_rate, nmul, 1);
remainder = rate - tmprate;
if (remainder) {
nfrac = DIV_ROUND_CLOSEST_ULL((uint64_t)remainder * (1 << 22),
parent_rate);
tmprate += DIV_ROUND_CLOSEST_ULL((uint64_t)nfrac * parent_rate,
(1 << 22));
}
/* Check if resulted rate is a valid. */
if (tmprate < core->characteristics->core_output[0].min ||
tmprate > core->characteristics->core_output[0].max) {
return -ERANGE;
}
if (update) {
frac->mul = nmul - 1;
frac->frac = nfrac;
}
return tmprate;
}
static int sam9x60_clk_frac_pll_get_rate(const struct device *dev,
clock_control_subsys_t sys, uint32_t *rate)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
struct sam9x60_frac *frac = to_sam9x60_frac(core);
int retval = 0;
retval = clock_control_get_rate(frac->core.parent, NULL, rate);
if (retval) {
LOG_ERR("get parent clock rate failed.");
*rate = 0;
} else {
*rate = *rate * (frac->mul + 1) +
DIV_ROUND_CLOSEST_ULL((uint64_t)*rate * frac->frac, (1<<22));
}
LOG_DBG("clk %s: rate %d", dev->name, *rate);
return retval;
}
static DEVICE_API(clock_control, frac_pll_api) = {
.on = sam9x60_clk_frac_pll_on,
.off = sam9x60_clk_frac_pll_off,
.get_rate = sam9x60_clk_frac_pll_get_rate,
.get_status = sam9x60_clk_frac_pll_get_status,
};
int sam9x60_clk_register_frac_pll(pmc_registers_t *const pmc, struct k_spinlock *lock,
const char *name,
const struct device *parent, uint8_t id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, struct device **clk)
{
struct sam9x60_frac *frac;
uint32_t parent_rate;
k_spinlock_key_t key;
unsigned int val;
int ret = 0;
if (id > PLL_MAX_ID || !lock || !parent) {
return -EINVAL;
}
frac = &clocks_frac[clocks_frac_idx++];
if (clocks_frac_idx > ARRAY_SIZE(clocks_frac)) {
LOG_ERR("Array for PLL frac clock not enough");
return -ENOMEM;
}
*clk = &frac->core.clk;
(*clk)->name = name;
(*clk)->api = &frac_pll_api;
frac->core.parent = parent;
frac->core.id = id;
frac->core.characteristics = characteristics;
frac->core.layout = layout;
frac->core.pmc = pmc;
frac->core.lock = lock;
key = k_spin_lock(frac->core.lock);
if (sam9x60_pll_ready(pmc, id)) {
reg_update_bits(pmc->PMC_PLL_UPDT, PMC_PLL_UPDT_ID_Msk, id);
val = pmc->PMC_PLL_CTRL1;
frac->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val);
frac->frac = FIELD_GET(PMC_PLL_CTRL1_FRACR_MSK, val);
} else {
/*
* This means the PLL is not setup by bootloaders. In this
* case we need to set the minimum rate for it. Otherwise
* a clock child of this PLL may be enabled before setting
* its rate leading to enabling this PLL with unsupported
* rate. This will lead to PLL not being locked at all.
*/
ret = clock_control_get_rate(parent, NULL, &parent_rate);
if (ret) {
LOG_ERR("get parent clock rate failed.");
goto free;
}
long tmp = sam9x60_frac_pll_compute_mul_frac(&frac->core,
characteristics->core_output[0].min,
parent_rate, true);
if (tmp < 0) {
ret = -ENOTSUP;
goto free;
}
}
k_spin_unlock(frac->core.lock, key);
return ret;
free:
k_spin_unlock(frac->core.lock, key);
k_free(frac);
return ret;
}
/* This function should be called with spinlock acquired. */
static void sam9x60_div_pll_set_div(struct sam9x60_pll_core *core, uint32_t div, bool enable)
{
pmc_registers_t *pmc = core->pmc;
uint32_t ena_msk = enable ? core->layout->endiv_mask : 0;
uint32_t ena_val = enable ? (1 << core->layout->endiv_shift) : 0;
reg_update_bits(pmc->PMC_PLL_CTRL0,
core->layout->div_mask | ena_msk,
(div << core->layout->div_shift) | ena_val);
reg_update_bits(pmc->PMC_PLL_UPDT,
PMC_PLL_UPDT_UPDATE_Msk | PMC_PLL_UPDT_ID_Msk,
PMC_PLL_UPDT_UPDATE_Msk | core->id);
while (!sam9x60_pll_ready(pmc, core->id)) {
k_busy_wait(1);
}
}
static int sam9x60_div_pll_set(struct sam9x60_pll_core *core)
{
struct sam9x60_div *div = to_sam9x60_div(core);
pmc_registers_t *pmc = core->pmc;
k_spinlock_key_t key;
uint32_t val, cdiv;
key = k_spin_lock(core->lock);
reg_update_bits(pmc->PMC_PLL_UPDT, PMC_PLL_UPDT_ID_Msk, core->id);
val = pmc->PMC_PLL_CTRL0;
cdiv = (val & core->layout->div_mask) >> core->layout->div_shift;
/* Stop if enabled an nothing changed. */
if (!!(val & core->layout->endiv_mask) && cdiv == div->div) {
goto unlock;
}
sam9x60_div_pll_set_div(core, div->div, 1);
unlock:
k_spin_unlock(core->lock, key);
return 0;
}
static int sam9x60_clk_div_pll_on(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
return sam9x60_div_pll_set(core);
}
static int sam9x60_clk_div_pll_off(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
pmc_registers_t *pmc = core->pmc;
k_spinlock_key_t key;
key = k_spin_lock(core->lock);
reg_update_bits(pmc->PMC_PLL_UPDT, PMC_PLL_UPDT_ID_Msk, core->id);
reg_update_bits(pmc->PMC_PLL_CTRL0, core->layout->endiv_mask, 0);
reg_update_bits(pmc->PMC_PLL_UPDT,
PMC_PLL_UPDT_UPDATE_Msk | PMC_PLL_UPDT_ID_Msk,
PMC_PLL_UPDT_UPDATE_Msk | core->id);
k_spin_unlock(core->lock, key);
return 0;
}
static enum clock_control_status sam9x60_clk_div_pll_get_status(const struct device *dev,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
pmc_registers_t *pmc = core->pmc;
k_spinlock_key_t key;
uint32_t val;
key = k_spin_lock(core->lock);
reg_update_bits(pmc->PMC_PLL_UPDT, PMC_PLL_UPDT_ID_Msk, core->id);
val = pmc->PMC_PLL_CTRL0;
k_spin_unlock(core->lock, key);
if (!!(val & core->layout->endiv_mask)) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
}
static int sam9x60_clk_div_pll_get_rate(const struct device *dev,
clock_control_subsys_t sys, uint32_t *rate)
{
ARG_UNUSED(sys);
struct sam9x60_pll_core *core = to_sam9x60_pll_core(dev);
struct sam9x60_div *div = to_sam9x60_div(core);
int retval = 0;
retval = clock_control_get_rate(div->core.parent, NULL, rate);
if (retval) {
LOG_ERR("get parent clock rate failed.");
*rate = 0;
} else {
*rate = DIV_ROUND_CLOSEST_ULL(*rate, div->div + 1);
}
LOG_DBG("clk %s: rate %d", dev->name, *rate);
return retval;
}
static DEVICE_API(clock_control, div_pll_api) = {
.on = sam9x60_clk_div_pll_on,
.off = sam9x60_clk_div_pll_off,
.get_rate = sam9x60_clk_div_pll_get_rate,
.get_status = sam9x60_clk_div_pll_get_status,
};
int sam9x60_clk_register_div_pll(pmc_registers_t *const pmc, struct k_spinlock *lock,
const char *name,
const struct device *parent, uint8_t id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout,
struct device **clk)
{
struct sam9x60_div *div;
k_spinlock_key_t key;
uint32_t val;
/* We only support one changeable PLL. */
if (id > PLL_MAX_ID || !lock || !parent) {
return -EINVAL;
}
div = &clocks_div[clocks_div_idx++];
if (clocks_div_idx > ARRAY_SIZE(clocks_div)) {
LOG_ERR("Array for PLL div clock not enough");
return -ENOMEM;
}
*clk = &div->core.clk;
(*clk)->name = name;
(*clk)->api = &div_pll_api;
div->core.parent = parent;
div->core.id = id;
div->core.characteristics = characteristics;
div->core.layout = layout;
div->core.pmc = pmc;
div->core.lock = lock;
key = k_spin_lock(div->core.lock);
reg_update_bits(pmc->PMC_PLL_UPDT, PMC_PLL_UPDT_ID_Msk, id);
val = pmc->PMC_PLL_CTRL0;
div->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val);
k_spin_unlock(div->core.lock, key);
return 0;
}

View file

@ -0,0 +1,141 @@
/*
* 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/spinlock.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clk_system, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
#define SYSTEM_MAX_ID 31
#define SYSTEM_MAX_NAME_SZ 32
#define to_clk_system(ptr) CONTAINER_OF(ptr, struct clk_system, clk)
struct clk_system {
struct device clk;
const struct device *parent;
pmc_registers_t *pmc;
uint8_t id;
};
static struct clk_system clocks_sys[8];
static uint32_t clocks_sys_idx;
static inline int is_pck(int id)
{
return (id >= 8) && (id <= 15);
}
static inline bool clk_system_ready(pmc_registers_t *pmc, int id)
{
uint32_t status = pmc->PMC_SR;
return !!(status & (1 << id));
}
static int clk_system_on(const struct device *dev, clock_control_subsys_t subsys)
{
ARG_UNUSED(subsys);
struct clk_system *sys = to_clk_system(dev);
sys->pmc->PMC_SCER = 1 << sys->id;
if (!is_pck(sys->id)) {
return 0;
}
while (!clk_system_ready(sys->pmc, sys->id)) {
k_busy_wait(1);
}
return 0;
}
static int clk_system_off(const struct device *dev, clock_control_subsys_t subsys)
{
ARG_UNUSED(subsys);
struct clk_system *sys = to_clk_system(dev);
sys->pmc->PMC_SCDR = 1 << sys->id;
return 0;
}
static int clk_system_get_rate(const struct device *dev,
clock_control_subsys_t subsys, uint32_t *rate)
{
ARG_UNUSED(subsys);
struct clk_system *sys = to_clk_system(dev);
return clock_control_get_rate(sys->parent, NULL, rate);
}
static enum clock_control_status clk_system_get_status(const struct device *dev,
clock_control_subsys_t subsys)
{
ARG_UNUSED(subsys);
struct clk_system *sys = to_clk_system(dev);
uint32_t status;
status = sys->pmc->PMC_SCSR;
if (!(status & (1 << sys->id))) {
return CLOCK_CONTROL_STATUS_OFF;
}
if (!is_pck(sys->id)) {
return CLOCK_CONTROL_STATUS_ON;
}
status = sys->pmc->PMC_SR;
if (!!(status & (1 << sys->id))) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
}
static DEVICE_API(clock_control, system_api) = {
.on = clk_system_on,
.off = clk_system_off,
.get_rate = clk_system_get_rate,
.get_status = clk_system_get_status,
};
int clk_register_system(pmc_registers_t *const pmc, const char *name,
const struct device *parent,
uint8_t id, struct device **clk)
{
struct clk_system *sys;
if (!parent || id > SYSTEM_MAX_ID) {
return -EINVAL;
}
sys = &clocks_sys[clocks_sys_idx++];
if (clocks_sys_idx > ARRAY_SIZE(clocks_sys)) {
LOG_ERR("Array for system clock not enough");
return -ENOMEM;
}
*clk = &sys->clk;
(*clk)->name = name;
(*clk)->api = &system_api;
sys->parent = parent;
sys->id = id;
sys->pmc = pmc;
return 0;
}

View file

@ -0,0 +1,154 @@
/*
* 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/spinlock.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(clk_utmi, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
/*
* The purpose of this clock is to generate a 480 MHz signal. A different
* rate can't be configured.
*/
#define UTMI_RATE MHZ(48)
struct clk_utmi {
struct device clk;
const struct device *parent;
pmc_registers_t *pmc;
};
#define to_clk_utmi(ptr) CONTAINER_OF(ptr, struct clk_utmi, clk)
static struct clk_utmi clock_utmi;
static int clk_utmi_sama7g5_get_rate(const struct device *dev,
clock_control_subsys_t sys, uint32_t *rate)
{
ARG_UNUSED(dev);
ARG_UNUSED(sys);
/* UTMI clk rate is fixed. */
*rate = UTMI_RATE;
return 0;
}
static int at91_clk_register_utmi_internal(pmc_registers_t *const pmc, const char *name,
const struct device *parent, const void *api,
struct device **clk)
{
struct clk_utmi *utmi = &clock_utmi;
if (!parent) {
return -EINVAL;
}
*clk = &utmi->clk;
(*clk)->name = name;
(*clk)->api = api;
utmi->parent = parent;
utmi->pmc = pmc;
return 0;
}
static int clk_utmi_sama7g5_on(const struct device *dev, clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_utmi *utmi = to_clk_utmi(dev);
uint32_t parent_rate;
uint32_t val;
int ret;
ret = clock_control_get_rate(utmi->parent, NULL, &parent_rate);
if (ret) {
LOG_ERR("get parent clock rate failed.");
return ret;
}
switch (parent_rate) {
case MHZ(16):
val = 0;
break;
case MHZ(20):
val = 2;
break;
case MHZ(24):
val = 3;
break;
case MHZ(32):
val = 5;
break;
default:
LOG_ERR("UTMICK: unsupported main_xtal rate\n");
return -EINVAL;
}
utmi->pmc->PMC_XTALF = val;
return 0;
}
static enum clock_control_status clk_utmi_sama7g5_get_status(const struct device *dev,
clock_control_subsys_t sys)
{
ARG_UNUSED(sys);
struct clk_utmi *utmi = to_clk_utmi(dev);
uint32_t parent_rate;
int ret;
ret = clock_control_get_rate(utmi->parent, NULL, &parent_rate);
if (ret) {
LOG_ERR("get parent clock rate failed.");
return ret;
}
switch (utmi->pmc->PMC_XTALF & 0x7) {
case 0:
if (parent_rate == MHZ(16)) {
return CLOCK_CONTROL_STATUS_ON;
}
break;
case 2:
if (parent_rate == MHZ(20)) {
return CLOCK_CONTROL_STATUS_ON;
}
break;
case 3:
if (parent_rate == MHZ(24)) {
return CLOCK_CONTROL_STATUS_ON;
}
break;
case 5:
if (parent_rate == MHZ(32)) {
return CLOCK_CONTROL_STATUS_ON;
}
break;
default:
break;
}
return CLOCK_CONTROL_STATUS_UNKNOWN;
}
static DEVICE_API(clock_control, sama7g5_utmi_api) = {
.on = clk_utmi_sama7g5_on,
.get_rate = clk_utmi_sama7g5_get_rate,
.get_status = clk_utmi_sama7g5_get_status,
};
int clk_register_utmi(pmc_registers_t *const pmc, const char *name,
const struct device *parent, struct device **clk)
{
return at91_clk_register_utmi_internal(pmc, name, parent, &sama7g5_utmi_api, clk);
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SOC_MICROCHIP_SAM_COMMON_PINCTRL_SOC_H_
#define ZEPHYR_SOC_MICROCHIP_SAM_COMMON_PINCTRL_SOC_H_
typedef struct pinctrl_soc_pin_t {
uint32_t pin_mux;
uint32_t bias_disable: 1;
uint32_t bias_pull_down: 1;
uint32_t bias_pull_up: 1;
uint32_t drive_open_drain: 1;
} pinctrl_soc_pin_t;
#define Z_PINCTRL_STATE_PIN_INIT(node_id, prop, idx) { \
.pin_mux = DT_PROP_BY_IDX(node_id, prop, idx), \
.bias_disable = DT_PROP(node_id, bias_disable), \
.bias_pull_down = DT_PROP(node_id, bias_pull_down), \
.bias_pull_up = DT_PROP(node_id, bias_pull_up), \
.drive_open_drain = DT_PROP(node_id, drive_open_drain), \
},
#define Z_PINCTRL_STATE_PINS_INIT(node_id, prop) { \
DT_FOREACH_CHILD_VARGS(DT_PHANDLE(node_id, prop), \
DT_FOREACH_PROP_ELEM, pinmux, \
Z_PINCTRL_STATE_PIN_INIT) \
}
#endif /* ZEPHYR_SOC_MICROCHIP_SAM_COMMON_PINCTRL_SOC_H_ */

View file

@ -0,0 +1,77 @@
/*
* 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(pmc_data, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
static struct pmc_data pmc_clocks;
struct device *sam_pmc_get_clock(const struct sam_clk_cfg *cfg,
struct pmc_data *ptr)
{
uint32_t idx = cfg->clock_id;
uint32_t type = cfg->clock_type;
switch (type) {
case PMC_TYPE_CORE:
if (idx < ptr->ncore) {
return ptr->chws[idx];
}
break;
case PMC_TYPE_SYSTEM:
if (idx < ptr->nsystem) {
return ptr->shws[idx];
}
break;
case PMC_TYPE_PERIPHERAL:
if (idx < ptr->nperiph) {
return ptr->phws[idx];
}
break;
case PMC_TYPE_GCK:
if (idx < ptr->ngck) {
return ptr->ghws[idx];
}
break;
case PMC_TYPE_PROGRAMMABLE:
if (idx < ptr->npck) {
return ptr->pchws[idx];
}
break;
default:
break;
}
return NULL;
}
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
unsigned int nperiph, unsigned int ngck,
unsigned int npck, struct device **table)
{
struct pmc_data *ptr = &pmc_clocks;
ptr->ncore = ncore;
ptr->chws = table;
ptr->nsystem = nsystem;
ptr->shws = ptr->chws + ncore;
ptr->nperiph = nperiph;
ptr->phws = ptr->shws + nsystem;
ptr->ngck = ngck;
ptr->ghws = ptr->phws + nperiph;
ptr->npck = npck;
ptr->pchws = ptr->ghws + ngck;
return ptr;
}

View file

@ -0,0 +1,184 @@
/*
* Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
*
* SPDX-License-Identifier: Apache-2.0
*
*/
#ifndef __SAM_COMMON_PMC_H_
#define __SAM_COMMON_PMC_H_
#include <zephyr/drivers/clock_control/mchp_sam_pmc.h>
#include <zephyr/device.h>
#include <zephyr/spinlock.h>
#define AT91_PMC_PLL_ACR_DEFAULT_UPLL 0x12020010 /* Default PLL ACR value for UPLL */
#define AT91_PMC_PLL_ACR_DEFAULT_PLLA 0x00020010 /* Default PLL ACR value for PLLA */
#define AT91_PMC_OSCBYPASS (1 << 1) /* Oscillator Bypass */
struct pmc_data {
unsigned int ncore;
struct device **chws;
unsigned int nsystem;
struct device **shws;
unsigned int nperiph;
struct device **phws;
unsigned int ngck;
struct device **ghws;
unsigned int npck;
struct device **pchws;
};
struct clk_range {
unsigned long min;
unsigned long max;
};
struct clk_master_layout {
uint32_t offset;
uint32_t mask;
uint8_t pres_shift;
};
struct clk_master_characteristics {
struct clk_range output;
uint32_t divisors[5];
uint8_t have_div3_pres;
};
struct clk_pll_layout {
uint32_t pllr_mask;
uint32_t mul_mask;
uint32_t frac_mask;
uint32_t div_mask;
uint32_t endiv_mask;
uint32_t mul_shift;
uint8_t frac_shift;
uint8_t div_shift;
uint8_t endiv_shift;
uint8_t div2;
};
struct clk_pll_characteristics {
struct clk_range input;
int num_output;
const struct clk_range *output;
const struct clk_range *core_output;
uint16_t *icpll;
uint8_t *out;
uint8_t upll : 1;
};
struct clk_programmable_layout {
uint8_t pres_mask;
uint8_t pres_shift;
uint8_t css_mask;
uint8_t have_slck_mck;
uint8_t is_pres_direct;
};
struct clk_pcr_layout {
uint32_t offset;
uint32_t cmd;
uint32_t gckcss_mask;
uint32_t pid_mask;
};
static inline void regmap_read(const volatile uint32_t *map,
uint32_t reg, uint32_t *val)
{
*val = *(volatile uint32_t *)((uint32_t)map + reg);
}
static inline void regmap_write(const volatile uint32_t *map,
uint32_t reg, uint32_t val)
{
*(volatile uint32_t *)((uint32_t)map + reg) = val;
}
static inline void regmap_update_bits(const volatile uint32_t *map,
uint32_t reg,
uint32_t mask, uint32_t val)
{
uint32_t r = *(volatile uint32_t *)((uint32_t)map + reg);
*(volatile uint32_t *)((uint32_t)map + reg) = (r & (~mask)) | val;
}
#define reg_update_bits(reg, mask, val) reg = (reg & (~mask)) | (val)
struct device *sam_pmc_get_clock(const struct sam_clk_cfg *cfg,
struct pmc_data *pmc_data);
struct pmc_data *pmc_data_allocate(unsigned int ncore, unsigned int nsystem,
unsigned int nperiph, unsigned int ngck,
unsigned int npck, struct device **table);
void sam_pmc_setup(const struct device *dev);
int clk_register_generated(pmc_registers_t *const pmc, struct k_spinlock *lock,
const struct clk_pcr_layout *layout,
const char *name,
const struct device **parents, uint32_t *mux_table,
uint8_t num_parents, uint8_t id,
const struct clk_range *range, int chg_pid, struct device **clk);
int clk_register_main_rc_osc(pmc_registers_t *const pmc, const char *name,
uint32_t frequency, struct device **clk);
int clk_register_main_osc(pmc_registers_t *const pmc, const char *name, uint32_t bypass,
uint32_t frequency, struct device **clk);
int clk_register_main(pmc_registers_t *const pmc, const char *name,
const struct device **parents, int num_parents, struct device **clk);
int clk_register_master_div(pmc_registers_t *const pmc, const char *name,
const struct device *parent, const struct clk_master_layout *layout,
const struct clk_master_characteristics *characteristics,
struct k_spinlock *lock, uint32_t safe_div, struct device **clk);
int clk_register_master(pmc_registers_t *const pmc,
const char *name, int num_parents,
const struct device **parents,
uint32_t *mux_table,
struct k_spinlock *lock, uint8_t id,
struct device **clk);
int clk_register_peripheral(pmc_registers_t *const pmc,
struct k_spinlock *lock,
const struct clk_pcr_layout *layout,
const char *name,
struct device *parent,
uint32_t id,
const struct clk_range *range,
struct device **clk);
int sam9x60_clk_register_div_pll(pmc_registers_t *const pmc, struct k_spinlock *lock,
const char *name,
const struct device *parent, uint8_t id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout,
struct device **clk);
int sam9x60_clk_register_frac_pll(pmc_registers_t *const pmc, struct k_spinlock *lock,
const char *name,
const struct device *parent, uint8_t id,
const struct clk_pll_characteristics *characteristics,
const struct clk_pll_layout *layout, struct device **clk);
int clk_register_programmable(pmc_registers_t *const pmc, const char *name,
const struct device **parents,
uint8_t num_parents, uint8_t id,
const struct clk_programmable_layout *layout,
uint32_t *mux_table, struct device **clk);
int clk_register_system(pmc_registers_t *const pmc, const char *name,
const struct device *parent,
uint8_t id, struct device **clk);
int clk_register_utmi(pmc_registers_t *const pmc, const char *name,
const struct device *parent, struct device **clk);
#endif /* __SAM_COMMON_PMC_H_ */

File diff suppressed because it is too large Load diff