diff --git a/CODEOWNERS b/CODEOWNERS index f73ffc25dd3..2d53bb2ebae 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -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 diff --git a/drivers/clock_control/CMakeLists.txt b/drivers/clock_control/CMakeLists.txt index d4102e338d3..a931e4e971c 100644 --- a/drivers/clock_control/CMakeLists.txt +++ b/drivers/clock_control/CMakeLists.txt @@ -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) diff --git a/drivers/clock_control/Kconfig b/drivers/clock_control/Kconfig index 56ad62cbc29..b8ffc8ce385 100644 --- a/drivers/clock_control/Kconfig +++ b/drivers/clock_control/Kconfig @@ -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 diff --git a/drivers/clock_control/Kconfig.rcar b/drivers/clock_control/Kconfig.rcar new file mode 100644 index 00000000000..21279627102 --- /dev/null +++ b/drivers/clock_control/Kconfig.rcar @@ -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. diff --git a/drivers/clock_control/clock_control_rcar_cpg_mssr.c b/drivers/clock_control/clock_control_rcar_cpg_mssr.c new file mode 100644 index 00000000000..dce9cd80aa3 --- /dev/null +++ b/drivers/clock_control/clock_control_rcar_cpg_mssr.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2020 IoT.bzh + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_rcar_cpg_mssr +#include +#include +#include +#include +#include + +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) diff --git a/include/drivers/clock_control/rcar_clock_control.h b/include/drivers/clock_control/rcar_clock_control.h new file mode 100644 index 00000000000..a40457bf309 --- /dev/null +++ b/include/drivers/clock_control/rcar_clock_control.h @@ -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 +#include + +struct rcar_cpg_clk { + uint32_t domain; + uint32_t module; + uint32_t rate; +}; + +#endif /* ZEPHYR_INCLUDE_DRIVERS_CLOCK_CONTROL_RCAR_CLOCK_CONTROL_H_ */