blob: 453408a31935c920a9d2d8f5fcbb3086eb0868da [file] [log] [blame]
/*
* 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;
}