summaryrefslogtreecommitdiff
path: root/bsp/rockchip/rk3500/driver/clk/clk-mmc-phase.c
blob: a27c921cc6a8d4b2dc0caa73e5d4ece5742291d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/*
 * Copyright (c) 2006-2024 RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-3-08      GuEe-GUI     the first version
 */

#define ROCKCHIP_MMC_DELAY_SEL          RT_BIT(11)
#define ROCKCHIP_MMC_DEGREE_OFFSET      1
#define ROCKCHIP_MMC_DEGREE_MASK        (0x3 << ROCKCHIP_MMC_DEGREE_OFFSET)
#define ROCKCHIP_MMC_DELAYNUM_OFFSET    3
#define ROCKCHIP_MMC_DELAYNUM_MASK      (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
/*
 * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
 * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg.
 */
#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60

#define PSECS_PER_SEC 1000000000000LL

#define RK3288_MMC_CLKGEN_DIV 2

rt_inline rt_ubase_t rk_clk_mmc_recalc(rt_ubase_t parent_rate)
{
    return parent_rate / RK3288_MMC_CLKGEN_DIV;
}

static rt_err_t rk_clk_mmc_set_phase(rt_ubase_t rate, void *reg, int shift,
        int degrees)
{
    rt_uint32_t raw_value, delay;
    rt_uint8_t nineties, remainder, delay_num;

    /*
     * The below calculation is based on the output clock from
     * MMC host to the card, which expects the phase clock inherits
     * the clock rate from its parent, namely the output clock
     * provider of MMC host. However, things may go wrong if
     * (1) It is orphan.
     * (2) It is assigned to the wrong parent.
     *
     * This check help debug the case (1), which seems to be the
     * most likely problem we often face and which makes it difficult
     * for people to debug unstable mmc tuning results.
     */
    if (!rate)
    {
        LOG_E("Invalid CLK rate in phase setting");

        return -RT_EINVAL;
    }

    nineties = degrees / 90;
    remainder = (degrees % 90);

    /*
     * Due to the inexact nature of the "fine" delay, we might
     * actually go non-monotonic.  We don't go _too_ monotonic
     * though, so we should be OK.  Here are options of how we may
     * work:
     *
     * Ideally we end up with:
     *   1.0, 2.0, ..., 69.0, 70.0, ...,  89.0, 90.0
     *
     * On one extreme (if delay is actually 44ps):
     *   .73, 1.5, ..., 50.6, 51.3, ...,  65.3, 90.0
     * The other (if delay is actually 77ps):
     *   1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90
     *
     * It's possible we might make a delay that is up to 25
     * degrees off from what we think we're making.  That's OK
     * though because we should be REALLY far from any bad range.
     */

    /*
     * Convert to delay; do a little extra work to make sure we
     * don't overflow 32-bit / 64-bit numbers.
     */
    delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
    delay *= remainder;
    delay = RT_DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
                (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));

    delay_num = (rt_uint8_t)rt_min_t(rt_uint32_t, delay, 255);

    raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
    raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
    raw_value |= nineties;
    HWREG32(reg) = HIWORD_UPDATE(raw_value, 0x07ff, shift);

    return RT_EOK;
}

static rt_base_t rk_clk_mmc_get_phase(rt_ubase_t rate, void *reg, int shift)
{
    rt_uint16_t degrees;
    rt_uint32_t raw_value, delay_num = 0;

    /* Constant signal, no measurable phase shift */
    if (!rate)
    {
        return 0;
    }

    raw_value = HWREG32(reg) >> shift;
    degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;

    if (raw_value & ROCKCHIP_MMC_DELAY_SEL)
    {
        /* degrees/delaynum * 1000000 */
        rt_ubase_t factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
                36 * (rate / 10000);

        delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
        delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
        degrees += RT_DIV_ROUND_CLOSEST(delay_num * factor, 1000000);
    }

    return degrees % 360;
}