blob: 711b685cd7bf6a22831a5aff807a251f370d0976 [file] [log] [blame]
Peng Fan690eea12021-08-07 16:00:45 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2021 NXP
4 */
5
6#include <common.h>
7#include <div64.h>
8#include <asm/io.h>
9#include <errno.h>
10#include <asm/arch/imx-regs.h>
11#include <asm/arch/pcc.h>
12#include <asm/arch/cgc.h>
13#include <asm/arch/sys_proto.h>
14
15#define cgc1_clk_TYPES 2
16#define cgc1_clk_NUM 8
17
18static enum cgc1_clk pcc3_clksrc[][8] = {
19 {
20 },
21 { DUMMY0_CLK,
22 LPOSC,
23 SOSC_DIV2,
24 FRO_DIV2,
25 XBAR_BUSCLK,
26 PLL3_PFD1_DIV1,
27 PLL3_PFD0_DIV2,
28 PLL3_PFD0_DIV1
29 }
30};
31
32static enum cgc1_clk pcc4_clksrc[][8] = {
33 {
34 DUMMY0_CLK,
35 SOSC_DIV1,
36 FRO_DIV1,
37 PLL3_PFD3_DIV2,
38 PLL3_PFD3_DIV1,
39 PLL3_PFD2_DIV2,
40 PLL3_PFD2_DIV1,
41 PLL3_PFD1_DIV2
42 },
43 {
44 DUMMY0_CLK,
45 DUMMY1_CLK,
46 LPOSC,
47 SOSC_DIV2,
48 FRO_DIV2,
49 XBAR_BUSCLK,
50 PLL3_VCODIV,
51 PLL3_PFD0_DIV1
52 }
53};
54
55static struct pcc_entry pcc3_arrays[] = {
56 {PCC3_RBASE, DMA1_MP_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
57 {PCC3_RBASE, DMA1_CH0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
58 {PCC3_RBASE, DMA1_CH1_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
59 {PCC3_RBASE, DMA1_CH2_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
60 {PCC3_RBASE, DMA1_CH3_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
61 {PCC3_RBASE, DMA1_CH4_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
62 {PCC3_RBASE, DMA1_CH5_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
63 {PCC3_RBASE, DMA1_CH6_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
64 {PCC3_RBASE, DMA1_CH7_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
65 {PCC3_RBASE, DMA1_CH8_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
66 {PCC3_RBASE, DMA1_CH9_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
67 {PCC3_RBASE, DMA1_CH10_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
68 {PCC3_RBASE, DMA1_CH11_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
69 {PCC3_RBASE, DMA1_CH12_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
70 {PCC3_RBASE, DMA1_CH13_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
71 {PCC3_RBASE, DMA1_CH14_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
72 {PCC3_RBASE, DMA1_CH15_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
73 {PCC3_RBASE, DMA1_CH16_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
74 {PCC3_RBASE, DMA1_CH17_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
75 {PCC3_RBASE, DMA1_CH18_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
76 {PCC3_RBASE, DMA1_CH19_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
77 {PCC3_RBASE, DMA1_CH20_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
78 {PCC3_RBASE, DMA1_CH21_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
79 {PCC3_RBASE, DMA1_CH22_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
80 {PCC3_RBASE, DMA1_CH23_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
81 {PCC3_RBASE, DMA1_CH24_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
82 {PCC3_RBASE, DMA1_CH25_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
83 {PCC3_RBASE, DMA1_CH26_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
84 {PCC3_RBASE, DMA1_CH27_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
85 {PCC3_RBASE, DMA1_CH28_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
86 {PCC3_RBASE, DMA1_CH29_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
87 {PCC3_RBASE, DMA1_CH30_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
88 {PCC3_RBASE, DMA1_CH31_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
89 {PCC3_RBASE, MU0_B_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
90 {PCC3_RBASE, MU3_A_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
91 {PCC3_RBASE, LLWU1_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
92 {PCC3_RBASE, UPOWER_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
93 {PCC3_RBASE, WDOG3_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
94 {PCC3_RBASE, WDOG4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
95 {PCC3_RBASE, XRDC_MGR_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
96 {PCC3_RBASE, SEMA42_1_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV, PCC_NO_RST_B},
97 {PCC3_RBASE, ROMCP1_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B},
98 {PCC3_RBASE, LPIT1_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
99 {PCC3_RBASE, TPM4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
100 {PCC3_RBASE, TPM5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
101 {PCC3_RBASE, FLEXIO1_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
102 {PCC3_RBASE, I3C2_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
103 {PCC3_RBASE, LPI2C4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
104 {PCC3_RBASE, LPI2C5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
105 {PCC3_RBASE, LPUART4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
106 {PCC3_RBASE, LPUART5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
107 {PCC3_RBASE, LPSPI4_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
108 {PCC3_RBASE, LPSPI5_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B},
109 {}
110};
111
112static struct pcc_entry pcc4_arrays[] = {
113 {PCC4_RBASE, FLEXSPI2_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
114 {PCC4_RBASE, TPM6_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
115 {PCC4_RBASE, TPM7_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
116 {PCC4_RBASE, LPI2C6_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
117 {PCC4_RBASE, LPI2C7_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
118 {PCC4_RBASE, LPUART6_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
119 {PCC4_RBASE, LPUART7_PCC4_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV, PCC_HAS_RST_B },
120 {PCC4_RBASE, SAI4_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
121 {PCC4_RBASE, SAI5_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
122 {PCC4_RBASE, PCTLE_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
123 {PCC4_RBASE, PCTLF_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
124 {PCC4_RBASE, SDHC0_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
125 {PCC4_RBASE, SDHC1_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
126 {PCC4_RBASE, SDHC2_PCC4_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV, PCC_HAS_RST_B },
127 {PCC4_RBASE, USB0_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
128 {PCC4_RBASE, USBPHY_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
129 {PCC4_RBASE, USB1_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
130 {PCC4_RBASE, USB1PHY_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
131 {PCC4_RBASE, USB_XBAR_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
132 {PCC4_RBASE, ENET_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_HAS_RST_B },
133 {PCC4_RBASE, SFA1_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
134 {PCC4_RBASE, RGPIOE_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
135 {PCC4_RBASE, RGPIOF_PCC4_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV, PCC_NO_RST_B },
136 {}
137};
138
139static int find_pcc_entry(int pcc_controller, int pcc_clk_slot, struct pcc_entry **out)
140{
141 struct pcc_entry *pcc_array;
142 int index = 0;
143
144 switch (pcc_controller) {
145 case 3:
146 pcc_array = pcc3_arrays;
147 *out = &pcc3_arrays[0];
148 break;
149 case 4:
150 pcc_array = pcc4_arrays;
151 *out = &pcc4_arrays[0];
152 break;
153 default:
154 printf("Not supported pcc_controller: %d\n", pcc_controller);
155 return -EINVAL;
156 }
157
158 while (pcc_array->pcc_base) {
159 if (pcc_array->pcc_slot == pcc_clk_slot)
160 return index;
161
162 pcc_array++;
163 index++;
164 }
165
166 return -ENOENT;
167}
168
169int pcc_clock_enable(int pcc_controller, int pcc_clk_slot, bool enable)
170{
171 u32 val;
172 void __iomem *reg;
173 int clk;
174 struct pcc_entry *pcc_array;
175
176 clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
177 if (clk < 0)
178 return -EINVAL;
179
180 reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
181
182 val = readl(reg);
183
184 debug("%s: clk %d, reg 0x%p, val 0x%x, enable %d\n", __func__, clk, reg, val, enable);
185
186 if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK))
187 return -EPERM;
188
189 if (enable)
190 val |= PCC_CGC_MASK;
191 else
192 val &= ~PCC_CGC_MASK;
193
194 writel(val, reg);
195
196 debug("%s: val 0x%x\n", __func__, val);
197
198 return 0;
199}
200
201/* The clock source select needs clock is disabled */
202int pcc_clock_sel(int pcc_controller, int pcc_clk_slot, enum cgc1_clk src)
203{
204 u32 val, i, clksrc_type;
205 void __iomem *reg;
206 struct pcc_entry *pcc_array;
207 enum cgc1_clk *cgc1_clk_array;
208 int clk;
209
210 clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
211 if (clk < 0)
212 return -EINVAL;
213
214 reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
215
216 clksrc_type = pcc_array[clk].clksrc;
217 if (clksrc_type >= CLKSRC_NO_PCS) {
218 printf("No PCS field for the PCC %d, clksrc type %d\n",
219 clk, clksrc_type);
220 return -EPERM;
221 }
222
223 if (pcc_controller == 3)
224 cgc1_clk_array = pcc3_clksrc[clksrc_type];
225 else
226 cgc1_clk_array = pcc4_clksrc[clksrc_type];
227
228 for (i = 0; i < cgc1_clk_NUM; i++) {
229 if (cgc1_clk_array[i] == src) {
230 /* Find the clock src, then set it to PCS */
231 break;
232 }
233 }
234
235 if (i == cgc1_clk_NUM) {
236 printf("No parent in PCS of PCC %d, invalid scg_clk %d\n", clk, src);
237 return -EINVAL;
238 }
239
240 val = readl(reg);
241
242 debug("%s: clk %d, reg 0x%p, val 0x%x, clksrc_type %d\n",
243 __func__, clk, reg, val, clksrc_type);
244
245 if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
246 (val & PCC_CGC_MASK)) {
247 printf("Not permit to select clock source val = 0x%x\n", val);
248 return -EPERM;
249 }
250
251 val &= ~PCC_PCS_MASK;
252 val |= i << PCC_PCS_OFFSET;
253
254 writel(val, reg);
255
256 debug("%s: val 0x%x\n", __func__, val);
257
258 return 0;
259}
260
261int pcc_clock_div_config(int pcc_controller, int pcc_clk_slot, bool frac, u8 div)
262{
263 u32 val;
264 void __iomem *reg;
265 struct pcc_entry *pcc_array;
266 int clk;
267
268 clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
269 if (clk < 0)
270 return -EINVAL;
271
272 reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
273
274 if (div > 8 || (div == 1 && frac != 0))
275 return -EINVAL;
276
277 if (pcc_array[clk].div >= PCC_NO_DIV) {
278 printf("No DIV/FRAC field for the PCC %d\n", clk);
279 return -EPERM;
280 }
281
282 val = readl(reg);
283
284 if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) ||
285 (val & PCC_CGC_MASK)) {
286 printf("Not permit to set div/frac val = 0x%x\n", val);
287 return -EPERM;
288 }
289
290 if (frac)
291 val |= PCC_FRAC_MASK;
292 else
293 val &= ~PCC_FRAC_MASK;
294
295 val &= ~PCC_PCD_MASK;
296 val |= (div - 1) & PCC_PCD_MASK;
297
298 writel(val, reg);
299
300 return 0;
301}
302
303bool pcc_clock_is_enable(int pcc_controller, int pcc_clk_slot)
304{
305 u32 val;
306 void __iomem *reg;
307 struct pcc_entry *pcc_array;
308 int clk;
309
310 clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
311 if (clk < 0)
312 return -EINVAL;
313
314 reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
315 val = readl(reg);
316
317 if ((val & PCC_INUSE_MASK) || (val & PCC_CGC_MASK))
318 return true;
319
320 return false;
321}
322
323int pcc_clock_get_clksrc(int pcc_controller, int pcc_clk_slot, enum cgc1_clk *src)
324{
325 u32 val, clksrc_type;
326 void __iomem *reg;
327 struct pcc_entry *pcc_array;
328 int clk;
329 enum cgc1_clk *cgc1_clk_array;
330
331 clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
332 if (clk < 0)
333 return -EINVAL;
334
335 clksrc_type = pcc_array[clk].clksrc;
336 if (clksrc_type >= CLKSRC_NO_PCS) {
337 printf("No PCS field for the PCC %d, clksrc type %d\n",
338 pcc_clk_slot, clksrc_type);
339 return -EPERM;
340 }
341
342 reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
343
344 val = readl(reg);
345
346 debug("%s: clk %d, reg 0x%p, val 0x%x, type %d\n",
347 __func__, pcc_clk_slot, reg, val, clksrc_type);
348
349 if (!(val & PCC_PR_MASK)) {
350 printf("This pcc slot is not present = 0x%x\n", val);
351 return -EPERM;
352 }
353
354 val &= PCC_PCS_MASK;
355 val = (val >> PCC_PCS_OFFSET);
356
357 if (!val) {
358 printf("Clock source is off\n");
359 return -EIO;
360 }
361
362 if (pcc_controller == 3)
363 cgc1_clk_array = pcc3_clksrc[clksrc_type];
364 else
365 cgc1_clk_array = pcc4_clksrc[clksrc_type];
366
367 *src = cgc1_clk_array[val];
368
369 debug("%s: parent cgc1 clk %d\n", __func__, *src);
370
371 return 0;
372}
373
374int pcc_reset_peripheral(int pcc_controller, int pcc_clk_slot, bool reset)
375{
376 u32 val;
377 void __iomem *reg;
378 struct pcc_entry *pcc_array;
379 int clk;
380
381 clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
382 if (clk < 0)
383 return -EINVAL;
384
385 if (pcc_array[clk].rst_b == PCC_NO_RST_B)
386 return 0;
387
388 reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base + pcc_array[clk].pcc_slot * 4);
389
390 val = readl(reg);
391
392 debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val);
393
394 if (!(val & PCC_PR_MASK)) {
395 printf("This pcc slot is not present = 0x%x\n", val);
396 return -EPERM;
397 }
398
399 if (reset)
400 val &= ~BIT(28);
401 else
402 val |= BIT(28);
403
404 writel(val, reg);
405
406 debug("%s: clk %d, reg 0x%p, val 0x%x\n", __func__, pcc_clk_slot, reg, val);
407
408 return 0;
409}
410
411u32 pcc_clock_get_rate(int pcc_controller, int pcc_clk_slot)
412{
413 u32 val, rate, frac, div;
414 void __iomem *reg;
415 enum cgc1_clk parent;
416 int ret;
417 int clk;
418 struct pcc_entry *pcc_array;
419
420 clk = find_pcc_entry(pcc_controller, pcc_clk_slot, &pcc_array);
421 if (clk < 0)
422 return -EINVAL;
423
424 ret = pcc_clock_get_clksrc(pcc_controller, pcc_clk_slot, &parent);
425 if (ret)
426 return 0;
427
428 rate = cgc1_clk_get_rate(parent);
429
430 debug("%s: parent rate %u\n", __func__, rate);
431
432 if (pcc_array[clk].div == PCC_HAS_DIV) {
433 reg = (void __iomem *)(uintptr_t)(pcc_array[clk].pcc_base +
434 pcc_array[clk].pcc_slot * 4);
435 val = readl(reg);
436
437 frac = (val & PCC_FRAC_MASK) >> PCC_FRAC_OFFSET;
438 div = (val & PCC_PCD_MASK) >> PCC_PCD_OFFSET;
439
440 /*
441 * Theoretically don't have overflow in the calc,
442 * the rate won't exceed 2G
443 */
444 rate = rate * (frac + 1) / (div + 1);
445 }
446
447 debug("%s: rate %u\n", __func__, rate);
448 return rate;
449}