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>
160 lines
3.7 KiB
C
160 lines
3.7 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/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;
|
|
}
|