1 /*
2 * Copyright (c) 2022 Teslabs Engineering S.L.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT gd_gd32_cctl
8
9 #include <stdint.h>
10
11 #include <zephyr/arch/cpu.h>
12 #include <zephyr/device.h>
13 #include <zephyr/devicetree.h>
14 #include <zephyr/drivers/clock_control.h>
15 #include <zephyr/drivers/clock_control/gd32.h>
16
17 #include <gd32_regs.h>
18
19 /** RCU offset (from id cell) */
20 #define GD32_CLOCK_ID_OFFSET(id) (((id) >> 6U) & 0xFFU)
21 /** RCU configuration bit (from id cell) */
22 #define GD32_CLOCK_ID_BIT(id) ((id)&0x1FU)
23
24 #define CPU_FREQ DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency)
25
26 /** AHB prescaler exponents */
27 static const uint8_t ahb_exp[16] = {
28 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, 6U, 7U, 8U, 9U,
29 };
30 /** APB1 prescaler exponents */
31 static const uint8_t apb1_exp[8] = {
32 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U,
33 };
34 /** APB2 prescaler exponents */
35 static const uint8_t apb2_exp[8] = {
36 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U,
37 };
38
39 struct clock_control_gd32_config {
40 uint32_t base;
41 };
42
43 #if DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer)
44 /* timer identifiers */
45 #define TIMER_ID_OR_NONE(nodelabel) \
46 COND_CODE_1(DT_NODE_HAS_STATUS(DT_NODELABEL(nodelabel), okay), \
47 (DT_CLOCKS_CELL(DT_NODELABEL(nodelabel), id),), ())
48
49 static const uint16_t timer_ids[] = {
50 TIMER_ID_OR_NONE(timer0) /* */
51 TIMER_ID_OR_NONE(timer1) /* */
52 TIMER_ID_OR_NONE(timer2) /* */
53 TIMER_ID_OR_NONE(timer3) /* */
54 TIMER_ID_OR_NONE(timer4) /* */
55 TIMER_ID_OR_NONE(timer5) /* */
56 TIMER_ID_OR_NONE(timer6) /* */
57 TIMER_ID_OR_NONE(timer7) /* */
58 TIMER_ID_OR_NONE(timer8) /* */
59 TIMER_ID_OR_NONE(timer9) /* */
60 TIMER_ID_OR_NONE(timer10) /* */
61 TIMER_ID_OR_NONE(timer11) /* */
62 TIMER_ID_OR_NONE(timer12) /* */
63 TIMER_ID_OR_NONE(timer13) /* */
64 TIMER_ID_OR_NONE(timer14) /* */
65 TIMER_ID_OR_NONE(timer15) /* */
66 TIMER_ID_OR_NONE(timer16) /* */
67 };
68 #endif /* DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer) */
69
clock_control_gd32_on(const struct device * dev,clock_control_subsys_t sys)70 static int clock_control_gd32_on(const struct device *dev,
71 clock_control_subsys_t sys)
72 {
73 const struct clock_control_gd32_config *config = dev->config;
74 uint16_t id = *(uint16_t *)sys;
75
76 sys_set_bit(config->base + GD32_CLOCK_ID_OFFSET(id),
77 GD32_CLOCK_ID_BIT(id));
78
79 return 0;
80 }
81
clock_control_gd32_off(const struct device * dev,clock_control_subsys_t sys)82 static int clock_control_gd32_off(const struct device *dev,
83 clock_control_subsys_t sys)
84 {
85 const struct clock_control_gd32_config *config = dev->config;
86 uint16_t id = *(uint16_t *)sys;
87
88 sys_clear_bit(config->base + GD32_CLOCK_ID_OFFSET(id),
89 GD32_CLOCK_ID_BIT(id));
90
91 return 0;
92 }
93
clock_control_gd32_get_rate(const struct device * dev,clock_control_subsys_t sys,uint32_t * rate)94 static int clock_control_gd32_get_rate(const struct device *dev,
95 clock_control_subsys_t sys,
96 uint32_t *rate)
97 {
98 const struct clock_control_gd32_config *config = dev->config;
99 uint16_t id = *(uint16_t *)sys;
100 uint32_t cfg;
101 uint8_t psc;
102
103 cfg = sys_read32(config->base + RCU_CFG0_OFFSET);
104
105 switch (GD32_CLOCK_ID_OFFSET(id)) {
106 #if defined(CONFIG_SOC_SERIES_GD32F4XX)
107 case RCU_AHB1EN_OFFSET:
108 case RCU_AHB2EN_OFFSET:
109 case RCU_AHB3EN_OFFSET:
110 #else
111 case RCU_AHBEN_OFFSET:
112 #endif
113 psc = (cfg & RCU_CFG0_AHBPSC_MSK) >> RCU_CFG0_AHBPSC_POS;
114 *rate = CPU_FREQ >> ahb_exp[psc];
115 break;
116 case RCU_APB1EN_OFFSET:
117 #if !defined(CONFIG_SOC_SERIES_GD32VF103) && \
118 !defined(CONFIG_SOC_SERIES_GD32A50X) && \
119 !defined(CONFIG_SOC_SERIES_GD32L23X)
120 case RCU_ADDAPB1EN_OFFSET:
121 #endif
122 psc = (cfg & RCU_CFG0_APB1PSC_MSK) >> RCU_CFG0_APB1PSC_POS;
123 *rate = CPU_FREQ >> apb1_exp[psc];
124 break;
125 case RCU_APB2EN_OFFSET:
126 psc = (cfg & RCU_CFG0_APB2PSC_MSK) >> RCU_CFG0_APB2PSC_POS;
127 *rate = CPU_FREQ >> apb2_exp[psc];
128 break;
129 default:
130 return -ENOTSUP;
131 }
132
133 #if DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer)
134 /* handle timer clocks */
135 for (size_t i = 0U; i < ARRAY_SIZE(timer_ids); i++) {
136 if (id != timer_ids[i]) {
137 continue;
138 }
139
140 #if defined(CONFIG_SOC_SERIES_GD32F4XX)
141 uint32_t cfg1 = sys_read32(config->base + RCU_CFG1_OFFSET);
142
143 /*
144 * The TIMERSEL bit in RCU_CFG1 controls the clock frequency of
145 * all the timers connected to the APB1 and APB2 domains.
146 *
147 * Up to a certain threshold value of APB{1,2} prescaler, timer
148 * clock equals to CK_AHB. This threshold value depends on
149 * TIMERSEL setting (2 if TIMERSEL=0, 4 if TIMERSEL=1). Above
150 * threshold, timer clock is set to a multiple of the APB
151 * domain clock CK_APB{1,2} (2 if TIMERSEL=0, 4 if TIMERSEL=1).
152 */
153
154 /* TIMERSEL = 0 */
155 if ((cfg1 & RCU_CFG1_TIMERSEL_MSK) == 0U) {
156 if (psc <= 2U) {
157 *rate = CPU_FREQ;
158 } else {
159 *rate *= 2U;
160 }
161 /* TIMERSEL = 1 */
162 } else {
163 if (psc <= 4U) {
164 *rate = CPU_FREQ;
165 } else {
166 *rate *= 4U;
167 }
168 }
169 #else
170 /*
171 * If the APB prescaler equals 1, the timer clock frequencies
172 * are set to the same frequency as that of the APB domain.
173 * Otherwise, they are set to twice the frequency of the APB
174 * domain.
175 */
176 if (psc != 1U) {
177 *rate *= 2U;
178 }
179 #endif /* CONFIG_SOC_SERIES_GD32F4XX */
180 }
181 #endif /* DT_HAS_COMPAT_STATUS_OKAY(gd_gd32_timer) */
182
183 return 0;
184 }
185
186 static enum clock_control_status
clock_control_gd32_get_status(const struct device * dev,clock_control_subsys_t sys)187 clock_control_gd32_get_status(const struct device *dev,
188 clock_control_subsys_t sys)
189 {
190 const struct clock_control_gd32_config *config = dev->config;
191 uint16_t id = *(uint16_t *)sys;
192
193 if (sys_test_bit(config->base + GD32_CLOCK_ID_OFFSET(id),
194 GD32_CLOCK_ID_BIT(id)) != 0) {
195 return CLOCK_CONTROL_STATUS_ON;
196 }
197
198 return CLOCK_CONTROL_STATUS_OFF;
199 }
200
201 static struct clock_control_driver_api clock_control_gd32_api = {
202 .on = clock_control_gd32_on,
203 .off = clock_control_gd32_off,
204 .get_rate = clock_control_gd32_get_rate,
205 .get_status = clock_control_gd32_get_status,
206 };
207
208 static const struct clock_control_gd32_config config = {
209 .base = DT_REG_ADDR(DT_INST_PARENT(0)),
210 };
211
212 DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, &config, PRE_KERNEL_1,
213 CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
214 &clock_control_gd32_api);
215