/* * 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 #include #include #include #include #include #include "clock_control_renesas_cpg_mssr.h" #define LOG_LEVEL CONFIG_CLOCK_CONTROL_LOG_LEVEL #include 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)