blob: bb20ac705295bb0708143867060b0a12478cee62 [file] [log] [blame]
/*
* 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)