| /* |
| * 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) |