|
| 1 | +/* |
| 2 | + * Copyright (c) 2023 TOKITA Hiroshi <[email protected]> |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <string.h> |
| 8 | + |
| 9 | +#define DT_DRV_COMPAT renesas_ra_clock_generation_circuit |
| 10 | + |
| 11 | +#include <zephyr/drivers/clock_control.h> |
| 12 | +#include <zephyr/kernel.h> |
| 13 | +#include <soc.h> |
| 14 | +#include <zephyr/dt-bindings/clock/renesas-ra-cgc.h> |
| 15 | + |
| 16 | +#if DT_SAME_NODE(DT_INST_PROP(0, clock_source), DT_PATH(clocks, pll)) |
| 17 | +#define SYSCLK_SRC pll |
| 18 | +#elif DT_SAME_NODE(DT_INST_PROP(0, clock_source), DT_PATH(clocks, mosc)) |
| 19 | +#define SYSCLK_SRC moco |
| 20 | +#elif DT_SAME_NODE(DT_INST_PROP(0, clock_source), DT_PATH(clocks, sosc)) |
| 21 | +#define SYSCLK_SRC soco |
| 22 | +#elif DT_SAME_NODE(DT_INST_PROP(0, clock_source), DT_PATH(clocks, hoco)) |
| 23 | +#define SYSCLK_SRC hoco |
| 24 | +#elif DT_SAME_NODE(DT_INST_PROP(0, clock_source), DT_PATH(clocks, moco)) |
| 25 | +#define SYSCLK_SRC moco |
| 26 | +#elif DT_SAME_NODE(DT_INST_PROP(0, clock_source), DT_PATH(clocks, loco)) |
| 27 | +#define SYSCLK_SRC loco |
| 28 | +#else |
| 29 | +#error Unknown clock source |
| 30 | +#endif |
| 31 | + |
| 32 | +#define FREQ_iclk (clock_freqs[_CONCAT(SCRSCK_, SYSCLK_SRC)] / DT_INST_PROP(0, iclk_div)) |
| 33 | +#define FREQ_pclka (clock_freqs[_CONCAT(SCRSCK_, SYSCLK_SRC)] / DT_INST_PROP(0, pclka_div)) |
| 34 | +#define FREQ_pclkb (clock_freqs[_CONCAT(SCRSCK_, SYSCLK_SRC)] / DT_INST_PROP(0, pclkb_div)) |
| 35 | +#define FREQ_pclkc (clock_freqs[_CONCAT(SCRSCK_, SYSCLK_SRC)] / DT_INST_PROP(0, pclkc_div)) |
| 36 | +#define FREQ_pclkd (clock_freqs[_CONCAT(SCRSCK_, SYSCLK_SRC)] / DT_INST_PROP(0, pclkd_div)) |
| 37 | +#define FREQ_fclk (clock_freqs[_CONCAT(SCRSCK_, SYSCLK_SRC)] / DT_INST_PROP(0, fclk_div)) |
| 38 | + |
| 39 | +#define CLKSRC_FREQ(clk) DT_PROP(DT_PATH(clocks, clk), clock_frequency) |
| 40 | + |
| 41 | +#define IS_CLKSRC_ENABLED(clk) DT_NODE_HAS_STATUS(DT_PATH(clocks, clk), okay) |
| 42 | + |
| 43 | +#define SCKSCR_INIT_VALUE _CONCAT(CLKSRC_, SYSCLK_SRC) |
| 44 | + |
| 45 | +#define SCKDIV_ENABLED(clk) DT_INST_NODE_HAS_PROP(0, clk##_div) |
| 46 | +#define SCKDIV_VAL(clk) _CONCAT(SCKDIV_, DT_INST_PROP(0, clk##_div)) |
| 47 | +#define SCKDIV_POS(clk) _CONCAT(SCKDIV_POS_, clk) |
| 48 | + |
| 49 | +#define SCKDIVCR_BITS(clk) \ |
| 50 | + COND_CODE_1(SCKDIV_ENABLED(clk), ((SCKDIV_VAL(clk) & 0xFU) << SCKDIV_POS(clk)), (0U)) |
| 51 | + |
| 52 | +#define SCKDIVCR_INIT_VALUE \ |
| 53 | + (SCKDIVCR_BITS(iclk) | SCKDIVCR_BITS(pclka) | SCKDIVCR_BITS(pclkb) | \ |
| 54 | + SCKDIVCR_BITS(pclkc) | SCKDIVCR_BITS(pclkd) | SCKDIVCR_BITS(bclk) | SCKDIVCR_BITS(fclk)) |
| 55 | + |
| 56 | +#define HOCOWTCR_INIT_VALUE (6) |
| 57 | + |
| 58 | +/* |
| 59 | + * Required cycles for sub-clokc stabilizing. |
| 60 | + */ |
| 61 | +#define SUBCLK_STABILIZE_CYCLES 5 |
| 62 | + |
| 63 | +extern int z_clock_hw_cycles_per_sec; |
| 64 | + |
| 65 | +enum { |
| 66 | + CLKSRC_hoco = 0, |
| 67 | + CLKSRC_moco, |
| 68 | + CLKSRC_loco, |
| 69 | + CLKSRC_mosc, |
| 70 | + CLKSRC_sosc, |
| 71 | + CLKSRC_pll, |
| 72 | +}; |
| 73 | + |
| 74 | +enum { |
| 75 | + SCKDIV_1 = 0, |
| 76 | + SCKDIV_2, |
| 77 | + SCKDIV_4, |
| 78 | + SCKDIV_8, |
| 79 | + SCKDIV_16, |
| 80 | + SCKDIV_32, |
| 81 | + SCKDIV_64, |
| 82 | + SCKDIV_128, |
| 83 | + SCKDIV_3, |
| 84 | + SCKDIV_6, |
| 85 | + SCKDIV_12 |
| 86 | +}; |
| 87 | + |
| 88 | +enum { |
| 89 | + SCKDIV_POS_pclkd = 0x0U, |
| 90 | + SCKDIV_POS_pclkc = 0x4U, |
| 91 | + SCKDIV_POS_pclkb = 0x8U, |
| 92 | + SCKDIV_POS_pclka = 0xcU, |
| 93 | + SCKDIV_POS_bclk = 0x10U, |
| 94 | + SCKDIV_POS_pclke = 0x14U, |
| 95 | + SCKDIV_POS_iclk = 0x18U, |
| 96 | + SCKDIV_POS_fclk = 0x1cU |
| 97 | +}; |
| 98 | + |
| 99 | +enum { |
| 100 | + OSCSF_HOCOSF_POS = 0, |
| 101 | + OSCSF_MOSCSF_POS = 3, |
| 102 | + OSCSF_PLLSF_POS = 5, |
| 103 | +}; |
| 104 | + |
| 105 | +enum { |
| 106 | + OPCCR_OPCMTSF_POS = 4, |
| 107 | +}; |
| 108 | + |
| 109 | +static const uint32_t PRCR_KEY = 0xA500U; |
| 110 | +static const uint32_t PRCR_CLOCKS = 0x1U; |
| 111 | +static const uint32_t PRCR_LOW_POWER = 0x2U; |
| 112 | + |
| 113 | +enum { |
| 114 | +#if DT_INST_REG_SIZE_BY_NAME(0, mstp) == 16 |
| 115 | + MSTPCRA_OFFSET = -0x4, |
| 116 | +#else |
| 117 | + MSTPCRA_OFFSET = 0x0, |
| 118 | +#endif |
| 119 | + MSTPCRB_OFFSET = (MSTPCRA_OFFSET + 0x4), |
| 120 | + MSTPCRC_OFFSET = (MSTPCRB_OFFSET + 0x4), |
| 121 | + MSTPCRD_OFFSET = (MSTPCRC_OFFSET + 0x4), |
| 122 | + MSTPCRE_OFFSET = (MSTPCRD_OFFSET + 0x4), |
| 123 | +}; |
| 124 | + |
| 125 | +enum { |
| 126 | + SCKDIVCR_OFFSET = 0x021, |
| 127 | + SCKSCR_OFFSET = 0x026, |
| 128 | + MEMWAIT_OFFSET = 0x031, |
| 129 | + MOSCCR_OFFSET = 0x032, |
| 130 | + HOCOCR_OFFSET = 0x036, |
| 131 | + OSCSF_OFFSET = 0x03C, |
| 132 | + CKOCR_OFFSET = 0x03E, |
| 133 | + OPCCR_OFFSET = 0x0A0, |
| 134 | + HOCOWTCR_OFFSET = 0x0A5, |
| 135 | + PRCR_OFFSET = 0x3FE, |
| 136 | + SOSCCR_OFFSET = 0x480, |
| 137 | +}; |
| 138 | + |
| 139 | +enum { |
| 140 | + SCRSCK_hoco, |
| 141 | + SCRSCK_moco, |
| 142 | + SCRSCK_loco, |
| 143 | + SCRSCK_mosc, |
| 144 | + SCRSCK_sosc, |
| 145 | + SCRSCK_pll, |
| 146 | +}; |
| 147 | + |
| 148 | +static const int clock_freqs[] = { |
| 149 | + COND_CODE_1(IS_CLKSRC_ENABLED(hoco), (CLKSRC_FREQ(hoco)), (0)), |
| 150 | + COND_CODE_1(IS_CLKSRC_ENABLED(moco), (CLKSRC_FREQ(moco)), (0)), |
| 151 | + COND_CODE_1(IS_CLKSRC_ENABLED(loco), (CLKSRC_FREQ(loco)), (0)), |
| 152 | + COND_CODE_1(IS_CLKSRC_ENABLED(mosc), (CLKSRC_FREQ(mosc)), (0)), |
| 153 | + COND_CODE_1(IS_CLKSRC_ENABLED(sosc), (CLKSRC_FREQ(sosc)), (0)), |
| 154 | + COND_CODE_1(IS_CLKSRC_ENABLED(pll), |
| 155 | + (DT_PROP(DT_PHANDLE_BY_IDX(DT_PATH(clocks, pll), clocks, 0), clock_frequency) * |
| 156 | + DT_PROP(DT_PATH(clocks, pll), clock_mult) / |
| 157 | + DT_PROP(DT_PATH(clocks, pll), clock_div)), |
| 158 | + (0)), |
| 159 | +}; |
| 160 | + |
| 161 | +static uint32_t MSTP_read(size_t offset) |
| 162 | +{ |
| 163 | + return sys_read32(DT_INST_REG_ADDR_BY_NAME(0, mstp) + offset); |
| 164 | +} |
| 165 | + |
| 166 | +static void MSTP_write(size_t offset, uint32_t value) |
| 167 | +{ |
| 168 | + sys_write32(value, DT_INST_REG_ADDR_BY_NAME(0, mstp) + offset); |
| 169 | +} |
| 170 | + |
| 171 | +static uint8_t SYSTEM_read8(size_t offset) |
| 172 | +{ |
| 173 | + return sys_read8(DT_INST_REG_ADDR_BY_NAME(0, system) + offset); |
| 174 | +} |
| 175 | + |
| 176 | +static void SYSTEM_write8(size_t offset, uint8_t value) |
| 177 | +{ |
| 178 | + sys_write8(value, DT_INST_REG_ADDR_BY_NAME(0, system) + offset); |
| 179 | +} |
| 180 | + |
| 181 | +static void SYSTEM_write16(size_t offset, uint16_t value) |
| 182 | +{ |
| 183 | + sys_write16(value, DT_INST_REG_ADDR_BY_NAME(0, system) + offset); |
| 184 | +} |
| 185 | + |
| 186 | +static void SYSTEM_write32(size_t offset, uint32_t value) |
| 187 | +{ |
| 188 | + sys_write32(value, DT_INST_REG_ADDR_BY_NAME(0, system) + offset); |
| 189 | +} |
| 190 | + |
| 191 | +static int clock_control_ra_on(const struct device *dev, clock_control_subsys_t subsys) |
| 192 | +{ |
| 193 | + uint32_t clkid = (uint32_t)subsys; |
| 194 | + int lock = irq_lock(); |
| 195 | + |
| 196 | + MSTP_write(MSTPCRA_OFFSET + RA_CLOCK_GROUP(clkid), |
| 197 | + MSTP_read(MSTPCRB_OFFSET) & ~RA_CLOCK_BIT(clkid)); |
| 198 | + irq_unlock(lock); |
| 199 | + |
| 200 | + return 0; |
| 201 | +} |
| 202 | + |
| 203 | +static int clock_control_ra_off(const struct device *dev, clock_control_subsys_t subsys) |
| 204 | +{ |
| 205 | + uint32_t clkid = (uint32_t)subsys; |
| 206 | + int lock = irq_lock(); |
| 207 | + |
| 208 | + MSTP_write(MSTPCRA_OFFSET + RA_CLOCK_GROUP(clkid), |
| 209 | + MSTP_read(MSTPCRB_OFFSET) | RA_CLOCK_BIT(clkid)); |
| 210 | + irq_unlock(lock); |
| 211 | + |
| 212 | + return 0; |
| 213 | +} |
| 214 | + |
| 215 | +static int clock_control_ra_get_rate(const struct device *dev, clock_control_subsys_t subsys, |
| 216 | + uint32_t *rate) |
| 217 | +{ |
| 218 | + uint32_t clkid = (uint32_t)subsys; |
| 219 | + |
| 220 | + switch (clkid & 0xFFFFFF00) { |
| 221 | + case RA_CLOCK_SCI(0): |
| 222 | + *rate = FREQ_pclka; |
| 223 | + break; |
| 224 | + default: |
| 225 | + return -EINVAL; |
| 226 | + } |
| 227 | + |
| 228 | + return 0; |
| 229 | +} |
| 230 | + |
| 231 | +static const struct clock_control_driver_api ra_clock_control_driver_api = { |
| 232 | + .on = clock_control_ra_on, |
| 233 | + .off = clock_control_ra_off, |
| 234 | + .get_rate = clock_control_ra_get_rate, |
| 235 | +}; |
| 236 | + |
| 237 | +static void crude_busy_loop_impl(uint32_t cycles) |
| 238 | +{ |
| 239 | + __asm__ volatile(".align 8\n" |
| 240 | + "busy_loop:\n" |
| 241 | + " sub r0, r0, #1\n" |
| 242 | + " cmp r0, #0\n" |
| 243 | + " bne.n busy_loop\n"); |
| 244 | +} |
| 245 | + |
| 246 | +static inline void crude_busy_loop(uint32_t wait_us) |
| 247 | +{ |
| 248 | + static const uint64_t cycles_per_loop = 4; |
| 249 | + |
| 250 | + crude_busy_loop_impl(sys_clock_hw_cycles_per_sec() * wait_us / USEC_PER_SEC / |
| 251 | + cycles_per_loop); |
| 252 | +} |
| 253 | + |
| 254 | +static int clock_control_ra_init(const struct device *dev) |
| 255 | +{ |
| 256 | + uint8_t sysclk = SYSTEM_read8(SCKSCR_OFFSET); |
| 257 | + |
| 258 | + z_clock_hw_cycles_per_sec = clock_freqs[sysclk]; |
| 259 | + |
| 260 | + SYSTEM_write16(PRCR_OFFSET, PRCR_KEY | PRCR_CLOCKS | PRCR_LOW_POWER); |
| 261 | + |
| 262 | + if (clock_freqs[SCRSCK_hoco] == 64000000) { |
| 263 | + SYSTEM_write8(HOCOWTCR_OFFSET, HOCOWTCR_INIT_VALUE); |
| 264 | + } |
| 265 | + |
| 266 | + SYSTEM_write8(SOSCCR_OFFSET, !IS_CLKSRC_ENABLED(sosc)); |
| 267 | + SYSTEM_write8(MOSCCR_OFFSET, !IS_CLKSRC_ENABLED(mosc)); |
| 268 | + SYSTEM_write8(HOCOCR_OFFSET, !IS_CLKSRC_ENABLED(hoco)); |
| 269 | + |
| 270 | + if (IS_CLKSRC_ENABLED(sosc)) { |
| 271 | + crude_busy_loop(z_clock_hw_cycles_per_sec / clock_freqs[CLKSRC_sosc] * |
| 272 | + SUBCLK_STABILIZE_CYCLES); |
| 273 | + } |
| 274 | + |
| 275 | + if (IS_CLKSRC_ENABLED(mosc)) { |
| 276 | + while ((SYSTEM_read8(OSCSF_OFFSET) & BIT(OSCSF_MOSCSF_POS)) != |
| 277 | + BIT(OSCSF_MOSCSF_POS)) { |
| 278 | + ; |
| 279 | + } |
| 280 | + } |
| 281 | + |
| 282 | + if (IS_CLKSRC_ENABLED(hoco)) { |
| 283 | + while ((SYSTEM_read8(OSCSF_OFFSET) & BIT(OSCSF_HOCOSF_POS)) != |
| 284 | + BIT(OSCSF_HOCOSF_POS)) { |
| 285 | + ; |
| 286 | + } |
| 287 | + } |
| 288 | + |
| 289 | + SYSTEM_write32(SCKDIVCR_OFFSET, SCKDIVCR_INIT_VALUE); |
| 290 | + SYSTEM_write8(SCKSCR_OFFSET, SCKSCR_INIT_VALUE); |
| 291 | + |
| 292 | + z_clock_hw_cycles_per_sec = clock_freqs[sysclk]; |
| 293 | + |
| 294 | + SYSTEM_write8(OPCCR_OFFSET, 0); |
| 295 | + while ((SYSTEM_read8(OPCCR_OFFSET) & BIT(OPCCR_OPCMTSF_POS)) != 0) { |
| 296 | + ; |
| 297 | + } |
| 298 | + |
| 299 | + SYSTEM_write8(MEMWAIT_OFFSET, 1); |
| 300 | + SYSTEM_write16(PRCR_OFFSET, PRCR_KEY | PRCR_CLOCKS | PRCR_LOW_POWER); |
| 301 | + |
| 302 | + return 0; |
| 303 | +} |
| 304 | + |
| 305 | +DEVICE_DT_INST_DEFINE(0, &clock_control_ra_init, NULL, NULL, NULL, PRE_KERNEL_1, |
| 306 | + CONFIG_CLOCK_CONTROL_INIT_PRIORITY, &ra_clock_control_driver_api); |
0 commit comments