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:
parent
877c050599
commit
2ad6e4e376
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
10
drivers/clock_control/Kconfig.rcar
Normal file
10
drivers/clock_control/Kconfig.rcar
Normal 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.
|
||||
227
drivers/clock_control/clock_control_rcar_cpg_mssr.c
Normal file
227
drivers/clock_control/clock_control_rcar_cpg_mssr.c
Normal 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)
|
||||
21
include/drivers/clock_control/rcar_clock_control.h
Normal file
21
include/drivers/clock_control/rcar_clock_control.h
Normal 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_ */
|
||||
Loading…
Reference in New Issue
Block a user