1 /*
2  * Copyright (c) 2006-2024 RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2022-3-08      GuEe-GUI     the first version
9  */
10 
11 #define ROCKCHIP_MMC_DELAY_SEL          RT_BIT(11)
12 #define ROCKCHIP_MMC_DEGREE_OFFSET      1
13 #define ROCKCHIP_MMC_DEGREE_MASK        (0x3 << ROCKCHIP_MMC_DEGREE_OFFSET)
14 #define ROCKCHIP_MMC_DELAYNUM_OFFSET    3
15 #define ROCKCHIP_MMC_DELAYNUM_MASK      (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
16 /*
17  * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
18  * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
19  */
20 #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
21 
22 #define PSECS_PER_SEC 1000000000000LL
23 
24 #define RK3288_MMC_CLKGEN_DIV 2
25 
rk_clk_mmc_recalc(rt_ubase_t parent_rate)26 rt_inline rt_ubase_t rk_clk_mmc_recalc(rt_ubase_t parent_rate)
27 {
28     return parent_rate / RK3288_MMC_CLKGEN_DIV;
29 }
30 
rk_clk_mmc_set_phase(rt_ubase_t rate,void * reg,int shift,int degrees)31 static rt_err_t rk_clk_mmc_set_phase(rt_ubase_t rate, void *reg, int shift,
32         int degrees)
33 {
34     rt_uint32_t raw_value, delay;
35     rt_uint8_t nineties, remainder, delay_num;
36 
37     /*
38      * The below calculation is based on the output clock from
39      * MMC host to the card, which expects the phase clock inherits
40      * the clock rate from its parent, namely the output clock
41      * provider of MMC host. However, things may go wrong if
42      * (1) It is orphan.
43      * (2) It is assigned to the wrong parent.
44      *
45      * This check help debug the case (1), which seems to be the
46      * most likely problem we often face and which makes it difficult
47      * for people to debug unstable mmc tuning results.
48      */
49     if (!rate)
50     {
51         LOG_E("Invalid CLK rate in phase setting");
52 
53         return -RT_EINVAL;
54     }
55 
56     nineties = degrees / 90;
57     remainder = (degrees % 90);
58 
59     /*
60      * Due to the inexact nature of the "fine" delay, we might
61      * actually go non-monotonic.  We don't go _too_ monotonic
62      * though, so we should be OK.  Here are options of how we may
63      * work:
64      *
65      * Ideally we end up with:
66      *   1.0, 2.0, ..., 69.0, 70.0, ...,  89.0, 90.0
67      *
68      * On one extreme (if delay is actually 44ps):
69      *   .73, 1.5, ..., 50.6, 51.3, ...,  65.3, 90.0
70      * The other (if delay is actually 77ps):
71      *   1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90
72      *
73      * It's possible we might make a delay that is up to 25
74      * degrees off from what we think we're making.  That's OK
75      * though because we should be REALLY far from any bad range.
76      */
77 
78     /*
79      * Convert to delay; do a little extra work to make sure we
80      * don't overflow 32-bit / 64-bit numbers.
81      */
82     delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
83     delay *= remainder;
84     delay = RT_DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
85                 (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
86 
87     delay_num = (rt_uint8_t)rt_min_t(rt_uint32_t, delay, 255);
88 
89     raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
90     raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
91     raw_value |= nineties;
92     HWREG32(reg) = HIWORD_UPDATE(raw_value, 0x07ff, shift);
93 
94     return RT_EOK;
95 }
96 
rk_clk_mmc_get_phase(rt_ubase_t rate,void * reg,int shift)97 static rt_base_t rk_clk_mmc_get_phase(rt_ubase_t rate, void *reg, int shift)
98 {
99     rt_uint16_t degrees;
100     rt_uint32_t raw_value, delay_num = 0;
101 
102     /* Constant signal, no measurable phase shift */
103     if (!rate)
104     {
105         return 0;
106     }
107 
108     raw_value = HWREG32(reg) >> shift;
109     degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
110 
111     if (raw_value & ROCKCHIP_MMC_DELAY_SEL)
112     {
113         /* degrees/delaynum * 1000000 */
114         rt_ubase_t factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
115                 36 * (rate / 10000);
116 
117         delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
118         delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
119         degrees += RT_DIV_ROUND_CLOSEST(delay_num * factor, 1000000);
120     }
121 
122     return degrees % 360;
123 }
124