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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-3-08 GuEe-GUI the first version
*/
#include "clk-rk-mmc-phase.h"
#define ROCKCHIP_MMC_DELAY_SEL RT_BIT(10)
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
#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
static rt_ubase_t rockchip_mmc_recalc(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
{
return parent_rate / RK3288_MMC_CLKGEN_DIV;
}
static rt_err_t rockchip_mmc_set_phase(struct rt_clk_cell *cell, int degrees)
{
rt_ubase_t rate;
rt_uint32_t raw_value, delay;
rt_uint8_t nineties, remainder, delay_num;
struct rockchip_mmc_clk_cell *mmc_cell = cell_to_rockchip_mmc_clk_cell(cell);
struct rockchip_clk_cell *rk_cell = &mmc_cell->rk_cell;
rate = rt_clk_cell_get_rate(cell);
/*
* 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)
{
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(rk_cell->provider->reg_base + rk_cell->muxdiv_offset) =
HIWORD_UPDATE(raw_value, 0x07ff, rk_cell->div_shift);
return RT_EOK;
}
static rt_base_t rockchip_mmc_get_phase(struct rt_clk_cell *cell)
{
rt_ubase_t rate;
rt_uint16_t degrees;
rt_uint32_t raw_value, delay_num = 0;
struct rockchip_mmc_clk_cell *mmc_cell = cell_to_rockchip_mmc_clk_cell(cell);
struct rockchip_clk_cell *rk_cell = &mmc_cell->rk_cell;
rate = rt_clk_cell_get_rate(cell);
/* Constant signal, no measurable phase shift */
if (!rate)
{
return 0;
}
raw_value = HWREG32(rk_cell->provider->reg_base + rk_cell->muxdiv_offset) >> rk_cell->div_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;
}
const struct rt_clk_ops rockchip_mmc_clk_ops =
{
.recalc_rate = rockchip_mmc_recalc,
.set_phase = rockchip_mmc_set_phase,
.get_phase = rockchip_mmc_get_phase,
};
static rt_err_t rockchip_mmc_clk_rate_notify(struct rt_clk_notifier *notifier,
rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate)
{
struct rockchip_mmc_clk_cell *mmc_cell = rt_container_of(notifier, struct rockchip_mmc_clk_cell, notifier);
if (old_rate <= new_rate)
{
return RT_EOK;
}
if (msg == RT_CLK_MSG_PRE_RATE_CHANGE)
{
mmc_cell->cached_phase = rockchip_mmc_get_phase(&mmc_cell->rk_cell.cell);
}
else if (mmc_cell->cached_phase != -RT_EINVAL && msg == RT_CLK_MSG_POST_RATE_CHANGE)
{
rockchip_mmc_set_phase(&mmc_cell->rk_cell.cell, mmc_cell->cached_phase);
}
return RT_EOK;
}
void rockchip_mmc_clk_cell_setup(struct rockchip_clk_cell *rk_cell)
{
struct rt_clk_cell *cell = &rk_cell->cell;
struct rockchip_mmc_clk_cell *mmc_cell = cell_to_rockchip_mmc_clk_cell(cell);
mmc_cell->notifier.callback = rockchip_mmc_clk_rate_notify;
rt_clk_notifier_register(rt_clk_cell_get_clk(cell, RT_NULL), &mmc_cell->notifier);
}
|