drivers: clock_control: add R-Car CPG MSSR driver

Clock Pulse Generator, Module Standby Software Reset, are registers
presents in Renesas Gen3 SoC series.

MSSR is used to supply clock to the different modules, shuch as timer,
or UART, it's also possible to issue a reset the different module.

CPG registers allow to get the rate or to set some divider like for
the CAN clock.

Signed-off-by: Julien Massot <julien.massot@iot.bzh>
This commit is contained in:
Julien Massot 2021-01-15 16:10:46 +01:00 committed by Ioannis Glaropoulos
parent 877c050599
commit 2ad6e4e376
6 changed files with 262 additions and 0 deletions

View File

@ -184,6 +184,7 @@
/drivers/can/*mcp2515* @karstenkoenig
/drivers/clock_control/*nrf* @nordic-krch
/drivers/clock_control/*esp32* @extremegtx
/drivers/clock_control/*rcar* @julien-massot
/drivers/counter/ @nordic-krch
/drivers/console/ipm_console.c @finikorg
/drivers/console/semihost_console.c @luozhongyao

View File

@ -16,6 +16,7 @@ endif()
zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_RV32M1_PCC clock_control_rv32m1_pcc.c)
zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_ESP32 clock_control_esp32.c)
zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_LITEX clock_control_litex.c)
zephyr_sources_ifdef(CONFIG_CLOCK_CONTROL_RCAR_CPG_MSSR clock_control_rcar_cpg_mssr.c)
if(CONFIG_CLOCK_CONTROL_STM32_CUBE)
if(CONFIG_SOC_SERIES_STM32MP1X)

View File

@ -48,4 +48,6 @@ source "drivers/clock_control/Kconfig.esp32"
source "drivers/clock_control/Kconfig.litex"
source "drivers/clock_control/Kconfig.rcar"
endif # CLOCK_CONTROL

View File

@ -0,0 +1,10 @@
# Renesas RCar Gen3
# Copyright (c) 2021 IoT.bzh
# SPDX-License-Identifier: Apache-2.0
config CLOCK_CONTROL_RCAR_CPG_MSSR
bool "RCar CPG MSSR driver"
depends on SOC_SERIES_RCAR_GEN3
help
Enable support for Renesas RCar CPG MSSR driver.

View File

@ -0,0 +1,227 @@
/*
* Copyright (c) 2020 IoT.bzh
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT renesas_rcar_cpg_mssr
#include <errno.h>
#include <soc.h>
#include <drivers/clock_control.h>
#include <drivers/clock_control/rcar_clock_control.h>
#include <dt-bindings/clock/renesas_rcar_cpg.h>
struct rcar_mssr_config {
uint32_t base_address;
};
#define DEV_CFG(dev) ((struct rcar_mssr_config *)(dev->config))
static const uint16_t rmstpsr[] = {
0x110, 0x114, 0x118, 0x11c,
0x120, 0x124, 0x128, 0x12c,
0x980, 0x984, 0x988, 0x98c,
};
#define RMSTPSR(i) rmstpsr[i]
/* Software Reset Register offsets */
static const uint16_t srcr[] = {
0x0A0, 0x0A8, 0x0B0, 0x0B8,
0x0BC, 0x0C4, 0x1C8, 0x1CC,
0x920, 0x924, 0x928, 0x92C,
};
#define SRCR(i) srcr[i]
#define SRSTCLR(i) (0x940 + (i) * 4)
/* CPG write protect */
#define CPGWPR 0x0900
/* CAN-FD Clock Frequency Control Register */
#define CANFDCKCR 0x244
/* Clock stop bit */
#define CANFDCKCR_CKSTP BIT(8)
/* On H3,M3,E3 parent clock of CANFD has 800MHz rate */
#define CANFDCKCR_PARENT_CLK_RATE 800000000
#define CANFDCKCR_DIVIDER_MASK 0x1FF
#define S3D4_CLK_RATE 66600000
static void cpg_write(const struct rcar_mssr_config *config,
uint32_t reg, uint32_t val)
{
sys_write32(~val, config->base_address + CPGWPR);
sys_write32(val, config->base_address + reg);
/* Wait for at least one cycle of the RCLK clock (@ ca. 32 kHz) */
k_sleep(K_USEC(35));
}
static void cpg_reset(const struct rcar_mssr_config *config,
uint32_t reg, uint32_t bit)
{
cpg_write(config, SRCR(reg), BIT(bit));
cpg_write(config, SRSTCLR(reg), BIT(bit));
}
static int cpg_core_clock_endisable(const struct device *dev,
uint32_t module, uint32_t rate, bool enable)
{
const struct rcar_mssr_config *config = DEV_CFG(dev);
uint32_t divider;
unsigned int key;
int ret;
/* Only support CANFD core clock at the moment */
if (module != CPG_CORE_CLK_CANFD) {
return -EINVAL;
}
key = irq_lock();
if (enable) {
if ((CANFDCKCR_PARENT_CLK_RATE % rate) != 0) {
__ASSERT(true, "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) {
__ASSERT(true, "Can not generate %u "
"from CANFD parent clock", rate);
ret = -EINVAL;
goto unlock;
}
cpg_write(config, CANFDCKCR, divider);
} else {
cpg_write(config, CANFDCKCR, CANFDCKCR_CKSTP);
}
unlock:
irq_unlock(key);
return 0;
}
static int cpg_rmstp_clock_endisable(const struct device *dev,
uint32_t module, bool enable)
{
const struct rcar_mssr_config *config = DEV_CFG(dev);
uint32_t reg = module / 100;
uint32_t bit = module % 100;
uint32_t bitmask = BIT(bit);
uint32_t reg_val;
__ASSERT((bit < 32) && reg < ARRAY_SIZE(rmstpsr),
"Invalid module number for cpg clock: %d", module);
unsigned int key = irq_lock();
reg_val = sys_read32(config->base_address + RMSTPSR(reg));
if (enable) {
reg_val &= ~bitmask;
} else {
reg_val |= bitmask;
}
sys_write32(reg_val, config->base_address + RMSTPSR(reg));
if (!enable) {
cpg_reset(config, reg, bit);
}
irq_unlock(key);
return 0;
}
static int cpg_mssr_blocking_start(const struct device *dev,
clock_control_subsys_t sys)
{
struct rcar_cpg_clk *clk = (struct rcar_cpg_clk *)sys;
int ret = -EINVAL;
if (clk->domain == CPG_MOD) {
ret = cpg_rmstp_clock_endisable(dev, clk->module, true);
} else if (clk->domain == CPG_CORE) {
ret = cpg_core_clock_endisable(dev, clk->module, clk->rate,
true);
}
return ret;
}
static int cpg_mssr_stop(const struct device *dev,
clock_control_subsys_t sys)
{
struct rcar_cpg_clk *clk = (struct rcar_cpg_clk *)sys;
int ret = -EINVAL;
if (clk->domain == CPG_MOD) {
ret = cpg_rmstp_clock_endisable(dev, clk->module, false);
} else if (clk->domain == CPG_CORE) {
ret = cpg_core_clock_endisable(dev, clk->module, 0, false);
}
return ret;
}
static int cpg_get_rate(const struct device *dev,
clock_control_subsys_t sys,
uint32_t *rate)
{
const struct rcar_mssr_config *config = DEV_CFG(dev);
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 CPG_CORE_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 CPG_CORE_CLK_S3D4:
*rate = S3D4_CLK_RATE;
break;
default:
ret = -ENOTSUP;
break;
}
return ret;
}
static int rcar_cpg_mssr_init(const struct device *dev)
{
return 0;
}
static const struct clock_control_driver_api rcar_cpg_mssr_api = {
.on = cpg_mssr_blocking_start,
.off = cpg_mssr_stop,
.get_rate = cpg_get_rate,
};
#define RCAR_MSSR_INIT(inst) \
static struct rcar_mssr_config rcar_mssr##inst##_config = { \
.base_address = DT_INST_REG_ADDR(inst) \
}; \
\
DEVICE_DT_INST_DEFINE(inst, \
&rcar_cpg_mssr_init, \
device_pm_control_nop, \
NULL, &rcar_mssr##inst##_config, \
PRE_KERNEL_1, \
CONFIG_KERNEL_INIT_PRIORITY_OBJECTS, \
&rcar_cpg_mssr_api);
DT_INST_FOREACH_STATUS_OKAY(RCAR_MSSR_INIT)

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2016 Open-RnD Sp. z o.o.
* Copyright (c) 2016 BayLibre, SAS
* Copyright (c) 2017 Linaro Limited.
* Copyright (c) 2017 RnDity Sp. z o.o.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_RCAR_CLOCK_CONTROL_H_
#define ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_RCAR_CLOCK_CONTROL_H_
#include <drivers/clock_control.h>
#include <dt-bindings/clock/renesas_rcar_cpg.h>
struct rcar_cpg_clk {
uint32_t domain;
uint32_t module;
uint32_t rate;
};
#endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_RCAR_CLOCK_CONTROL_H_ */