/* * 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, \ NULL, \ NULL, &rcar_mssr##inst##_config, \ PRE_KERNEL_1, \ CONFIG_CLOCK_CONTROL_INIT_PRIORITY, \ &rcar_cpg_mssr_api); DT_INST_FOREACH_STATUS_OKAY(RCAR_MSSR_INIT)