164 lines
4.0 KiB
C
164 lines
4.0 KiB
C
/*
|
|
* Copyright (c) 2022 IoT.bzh
|
|
*
|
|
* r8a7795 Clock Pulse Generator / Module Standby and Software Reset
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT renesas_r8a7795_cpg_mssr
|
|
|
|
#include <errno.h>
|
|
#include <soc.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#include <zephyr/drivers/clock_control/renesas_cpg_mssr.h>
|
|
#include <zephyr/dt-bindings/clock/renesas_cpg_mssr.h>
|
|
#include <zephyr/dt-bindings/clock/r8a7795_cpg_mssr.h>
|
|
#include "clock_control_renesas_cpg_mssr.h"
|
|
|
|
#define LOG_LEVEL CONFIG_CLOCK_CONTROL_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(clock_control_rcar);
|
|
|
|
struct r8a7795_cpg_mssr_config {
|
|
mm_reg_t base_address;
|
|
};
|
|
|
|
int r8a7795_cpg_core_clock_endisable(uint32_t base_address, uint32_t module,
|
|
uint32_t rate, bool enable)
|
|
{
|
|
uint32_t divider;
|
|
unsigned int key;
|
|
int ret = 0;
|
|
|
|
/* Only support CANFD core clock at the moment */
|
|
if (module != R8A7795_CLK_CANFD) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
key = irq_lock();
|
|
|
|
if (enable) {
|
|
if (rate > 0) {
|
|
if ((CANFDCKCR_PARENT_CLK_RATE % rate) != 0) {
|
|
LOG_ERR("Can not generate %u from CANFD parent clock", rate);
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
divider = (CANFDCKCR_PARENT_CLK_RATE / rate) - 1;
|
|
if (divider > CANFDCKCR_DIVIDER_MASK) {
|
|
LOG_ERR("Can not generate %u from CANFD parent clock", rate);
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
rcar_cpg_write(base_address, CANFDCKCR, divider);
|
|
} else {
|
|
LOG_ERR("Can not enable a clock at %u Hz", rate);
|
|
ret = -EINVAL;
|
|
}
|
|
} else {
|
|
rcar_cpg_write(base_address, CANFDCKCR, CANFDCKCR_CKSTP);
|
|
}
|
|
|
|
unlock:
|
|
irq_unlock(key);
|
|
return ret;
|
|
}
|
|
|
|
int r8a7795_cpg_mssr_start_stop(const struct device *dev,
|
|
clock_control_subsys_t sys, bool enable)
|
|
{
|
|
const struct r8a7795_cpg_mssr_config *config = dev->config;
|
|
struct rcar_cpg_clk *clk = (struct rcar_cpg_clk *)sys;
|
|
uint32_t reg = clk->module / 100;
|
|
uint32_t bit = clk->module % 100;
|
|
int ret = -EINVAL;
|
|
|
|
__ASSERT((bit < 32) && reg < ARRAY_SIZE(mstpcr),
|
|
"Invalid module number for cpg clock: %d", clk->module);
|
|
|
|
if (clk->domain == CPG_MOD) {
|
|
ret = rcar_cpg_mstp_clock_endisable(config->base_address, bit, reg, enable);
|
|
} else if (clk->domain == CPG_CORE) {
|
|
ret = r8a7795_cpg_core_clock_endisable(config->base_address, clk->module,
|
|
clk->rate, enable);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int r8a7795_cpg_mssr_start(const struct device *dev,
|
|
clock_control_subsys_t sys)
|
|
{
|
|
return r8a7795_cpg_mssr_start_stop(dev, sys, true);
|
|
}
|
|
|
|
static int r8a7795_cpg_mssr_stop(const struct device *dev,
|
|
clock_control_subsys_t sys)
|
|
{
|
|
return r8a7795_cpg_mssr_start_stop(dev, sys, false);
|
|
}
|
|
|
|
static int r8a7795_cpg_get_rate(const struct device *dev,
|
|
clock_control_subsys_t sys,
|
|
uint32_t *rate)
|
|
{
|
|
const struct r8a7795_cpg_mssr_config *config = dev->config;
|
|
struct rcar_cpg_clk *clk = (struct rcar_cpg_clk *)sys;
|
|
uint32_t val;
|
|
int ret = 0;
|
|
|
|
if (clk->domain != CPG_CORE) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
switch (clk->module) {
|
|
case R8A7795_CLK_CANFD:
|
|
val = sys_read32(config->base_address + CANFDCKCR);
|
|
if (val & CANFDCKCR_CKSTP) {
|
|
*rate = 0;
|
|
} else {
|
|
val &= CANFDCKCR_DIVIDER_MASK;
|
|
*rate = CANFDCKCR_PARENT_CLK_RATE / (val + 1);
|
|
}
|
|
break;
|
|
case R8A7795_CLK_S3D4:
|
|
*rate = S3D4_CLK_RATE;
|
|
break;
|
|
default:
|
|
ret = -ENOTSUP;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int r8a7795_cpg_mssr_init(const struct device *dev)
|
|
{
|
|
ARG_UNUSED(dev);
|
|
return 0;
|
|
}
|
|
|
|
static const struct clock_control_driver_api r8a7795_cpg_mssr_api = {
|
|
.on = r8a7795_cpg_mssr_start,
|
|
.off = r8a7795_cpg_mssr_stop,
|
|
.get_rate = r8a7795_cpg_get_rate,
|
|
};
|
|
|
|
#define R8A7795_MSSR_INIT(inst) \
|
|
static struct r8a7795_cpg_mssr_config r8a7795_cpg_mssr##inst##_config = { \
|
|
.base_address = DT_INST_REG_ADDR(inst) \
|
|
}; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(inst, \
|
|
&r8a7795_cpg_mssr_init, \
|
|
NULL, \
|
|
NULL, &r8a7795_cpg_mssr##inst##_config, \
|
|
PRE_KERNEL_1, \
|
|
CONFIG_CLOCK_CONTROL_INIT_PRIORITY, \
|
|
&r8a7795_cpg_mssr_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(R8A7795_MSSR_INIT)
|