Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/dpll/zl3073x/ref.c
122918 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#include <linux/bitfield.h>
4
#include <linux/cleanup.h>
5
#include <linux/dev_printk.h>
6
#include <linux/string.h>
7
#include <linux/string_choices.h>
8
#include <linux/types.h>
9
10
#include "core.h"
11
#include "ref.h"
12
13
/**
14
* zl3073x_ref_freq_factorize - factorize given frequency
15
* @freq: input frequency
16
* @base: base frequency
17
* @mult: multiplier
18
*
19
* Checks if the given frequency can be factorized using one of the
20
* supported base frequencies. If so the base frequency and multiplier
21
* are stored into appropriate parameters if they are not NULL.
22
*
23
* Return: 0 on success, -EINVAL if the frequency cannot be factorized
24
*/
25
int
26
zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
27
{
28
static const u16 base_freqs[] = {
29
1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125,
30
128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000,
31
1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250,
32
6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250,
33
32000, 40000, 50000, 62500,
34
};
35
u32 div;
36
int i;
37
38
for (i = 0; i < ARRAY_SIZE(base_freqs); i++) {
39
div = freq / base_freqs[i];
40
41
if (div <= U16_MAX && (freq % base_freqs[i]) == 0) {
42
if (base)
43
*base = base_freqs[i];
44
if (mult)
45
*mult = div;
46
47
return 0;
48
}
49
}
50
51
return -EINVAL;
52
}
53
54
/**
55
* zl3073x_ref_state_fetch - fetch input reference state from hardware
56
* @zldev: pointer to zl3073x_dev structure
57
* @index: input reference index to fetch state for
58
*
59
* Function fetches state for the given input reference from hardware and
60
* stores it for later use.
61
*
62
* Return: 0 on success, <0 on error
63
*/
64
int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
65
{
66
struct zl3073x_ref *ref = &zldev->ref[index];
67
int rc;
68
69
/* For differential type inputs the N-pin reference shares
70
* part of the configuration with the P-pin counterpart.
71
*/
72
if (zl3073x_is_n_pin(index) && zl3073x_ref_is_diff(ref - 1)) {
73
struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
74
75
/* Copy the shared items from the P-pin */
76
ref->config = p_ref->config;
77
ref->esync_n_div = p_ref->esync_n_div;
78
ref->freq_base = p_ref->freq_base;
79
ref->freq_mult = p_ref->freq_mult;
80
ref->freq_ratio_m = p_ref->freq_ratio_m;
81
ref->freq_ratio_n = p_ref->freq_ratio_n;
82
ref->phase_comp = p_ref->phase_comp;
83
ref->sync_ctrl = p_ref->sync_ctrl;
84
85
return 0; /* Finish - no non-shared items for now */
86
}
87
88
guard(mutex)(&zldev->multiop_lock);
89
90
/* Read reference configuration */
91
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
92
ZL_REG_REF_MB_MASK, BIT(index));
93
if (rc)
94
return rc;
95
96
/* Read ref_config register */
97
rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref->config);
98
if (rc)
99
return rc;
100
101
/* Read frequency related registers */
102
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &ref->freq_base);
103
if (rc)
104
return rc;
105
rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &ref->freq_mult);
106
if (rc)
107
return rc;
108
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &ref->freq_ratio_m);
109
if (rc)
110
return rc;
111
rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &ref->freq_ratio_n);
112
if (rc)
113
return rc;
114
115
/* Read eSync and N-div rated registers */
116
rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &ref->esync_n_div);
117
if (rc)
118
return rc;
119
rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref->sync_ctrl);
120
if (rc)
121
return rc;
122
123
/* Read phase compensation register */
124
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev)) {
125
u32 val;
126
127
rc = zl3073x_read_u32(zldev, ZL_REG_REF_PHASE_OFFSET_COMP_32,
128
&val);
129
ref->phase_comp = val;
130
} else {
131
rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
132
&ref->phase_comp);
133
}
134
if (rc)
135
return rc;
136
137
dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
138
str_enabled_disabled(zl3073x_ref_is_enabled(ref)),
139
zl3073x_ref_is_diff(ref) ? "differential" : "single-ended");
140
141
return rc;
142
}
143
144
/**
145
* zl3073x_ref_state_get - get current input reference state
146
* @zldev: pointer to zl3073x_dev structure
147
* @index: input reference index to get state for
148
*
149
* Return: pointer to given input reference state
150
*/
151
const struct zl3073x_ref *
152
zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
153
{
154
return &zldev->ref[index];
155
}
156
157
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
158
const struct zl3073x_ref *ref)
159
{
160
struct zl3073x_ref *dref = &zldev->ref[index];
161
int rc;
162
163
guard(mutex)(&zldev->multiop_lock);
164
165
/* Read reference configuration into mailbox */
166
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
167
ZL_REG_REF_MB_MASK, BIT(index));
168
if (rc)
169
return rc;
170
171
/* Update mailbox with changed values */
172
if (dref->freq_base != ref->freq_base)
173
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE,
174
ref->freq_base);
175
if (!rc && dref->freq_mult != ref->freq_mult)
176
rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT,
177
ref->freq_mult);
178
if (!rc && dref->freq_ratio_m != ref->freq_ratio_m)
179
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M,
180
ref->freq_ratio_m);
181
if (!rc && dref->freq_ratio_n != ref->freq_ratio_n)
182
rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N,
183
ref->freq_ratio_n);
184
if (!rc && dref->esync_n_div != ref->esync_n_div)
185
rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
186
ref->esync_n_div);
187
if (!rc && dref->sync_ctrl != ref->sync_ctrl)
188
rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
189
ref->sync_ctrl);
190
if (!rc && dref->phase_comp != ref->phase_comp) {
191
if (zl3073x_dev_is_ref_phase_comp_32bit(zldev))
192
rc = zl3073x_write_u32(zldev,
193
ZL_REG_REF_PHASE_OFFSET_COMP_32,
194
ref->phase_comp);
195
else
196
rc = zl3073x_write_u48(zldev,
197
ZL_REG_REF_PHASE_OFFSET_COMP,
198
ref->phase_comp);
199
}
200
if (rc)
201
return rc;
202
203
/* Commit reference configuration */
204
rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
205
ZL_REG_REF_MB_MASK, BIT(index));
206
if (rc)
207
return rc;
208
209
/* After successful commit store new state */
210
dref->freq_base = ref->freq_base;
211
dref->freq_mult = ref->freq_mult;
212
dref->freq_ratio_m = ref->freq_ratio_m;
213
dref->freq_ratio_n = ref->freq_ratio_n;
214
dref->esync_n_div = ref->esync_n_div;
215
dref->sync_ctrl = ref->sync_ctrl;
216
dref->phase_comp = ref->phase_comp;
217
218
return 0;
219
}
220
221