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
zl3073x_ref_freq_factorize(u32 freq,u16 * base,u16 * mult)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_update - update input reference status from HW
56 * @zldev: pointer to zl3073x_dev structure
57 * @index: input reference index
58 *
59 * Return: 0 on success, <0 on error
60 */
zl3073x_ref_state_update(struct zl3073x_dev * zldev,u8 index)61 int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index)
62 {
63 struct zl3073x_ref *ref = &zldev->ref[index];
64
65 return zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(index),
66 &ref->mon_status);
67 }
68
69 /**
70 * zl3073x_ref_state_fetch - fetch input reference state from hardware
71 * @zldev: pointer to zl3073x_dev structure
72 * @index: input reference index to fetch state for
73 *
74 * Function fetches state for the given input reference from hardware and
75 * stores it for later use.
76 *
77 * Return: 0 on success, <0 on error
78 */
zl3073x_ref_state_fetch(struct zl3073x_dev * zldev,u8 index)79 int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
80 {
81 struct zl3073x_ref *ref = &zldev->ref[index];
82 int rc;
83
84 /* For differential type inputs the N-pin reference shares
85 * part of the configuration with the P-pin counterpart.
86 */
87 if (zl3073x_is_n_pin(index) && zl3073x_ref_is_diff(ref - 1)) {
88 struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
89
90 /* Copy the shared items from the P-pin */
91 ref->cfg = p_ref->cfg;
92 ref->inv = p_ref->inv;
93
94 return 0; /* Finish - no non-shared items for now */
95 }
96
97 /* Read reference status */
98 rc = zl3073x_ref_state_update(zldev, index);
99 if (rc)
100 return rc;
101
102 guard(mutex)(&zldev->multiop_lock);
103
104 /* Read reference configuration */
105 rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
106 ZL_REG_REF_MB_MASK, BIT(index));
107 if (rc)
108 return rc;
109
110 /* Read ref_config register */
111 rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref->config);
112 if (rc)
113 return rc;
114
115 /* Read frequency related registers */
116 rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &ref->freq_base);
117 if (rc)
118 return rc;
119 rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &ref->freq_mult);
120 if (rc)
121 return rc;
122 rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &ref->freq_ratio_m);
123 if (rc)
124 return rc;
125 rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &ref->freq_ratio_n);
126 if (rc)
127 return rc;
128
129 /* Read eSync and N-div rated registers */
130 rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &ref->esync_n_div);
131 if (rc)
132 return rc;
133 rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref->sync_ctrl);
134 if (rc)
135 return rc;
136
137 /* Read phase compensation register */
138 if (zl3073x_dev_is_ref_phase_comp_32bit(zldev)) {
139 u32 val;
140
141 rc = zl3073x_read_u32(zldev, ZL_REG_REF_PHASE_OFFSET_COMP_32,
142 &val);
143 ref->phase_comp = val;
144 } else {
145 rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
146 &ref->phase_comp);
147 }
148 if (rc)
149 return rc;
150
151 dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
152 str_enabled_disabled(zl3073x_ref_is_enabled(ref)),
153 zl3073x_ref_is_diff(ref) ? "differential" : "single-ended");
154
155 return rc;
156 }
157
158 /**
159 * zl3073x_ref_state_get - get current input reference state
160 * @zldev: pointer to zl3073x_dev structure
161 * @index: input reference index to get state for
162 *
163 * Return: pointer to given input reference state
164 */
165 const struct zl3073x_ref *
zl3073x_ref_state_get(struct zl3073x_dev * zldev,u8 index)166 zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
167 {
168 return &zldev->ref[index];
169 }
170
171 /**
172 * zl3073x_ref_state_set - commit input reference state changes to hardware
173 * @zldev: pointer to zl3073x_dev structure
174 * @index: input reference index to set state for
175 * @ref: desired reference state
176 *
177 * Validates that invariant fields have not been modified, skips the HW
178 * write if the mutable configuration is unchanged, and otherwise writes
179 * only the changed cfg fields to hardware via the mailbox interface.
180 *
181 * Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
182 */
zl3073x_ref_state_set(struct zl3073x_dev * zldev,u8 index,const struct zl3073x_ref * ref)183 int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
184 const struct zl3073x_ref *ref)
185 {
186 struct zl3073x_ref *dref = &zldev->ref[index];
187 int rc;
188
189 /* Reject attempts to change invariant fields (set at init only) */
190 if (WARN_ON(memcmp(&dref->inv, &ref->inv, sizeof(ref->inv))))
191 return -EINVAL;
192
193 /* Skip HW write if configuration hasn't changed */
194 if (!memcmp(&dref->cfg, &ref->cfg, sizeof(ref->cfg)))
195 return 0;
196
197 guard(mutex)(&zldev->multiop_lock);
198
199 /* Read reference configuration into mailbox */
200 rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
201 ZL_REG_REF_MB_MASK, BIT(index));
202 if (rc)
203 return rc;
204
205 /* Update mailbox with changed values */
206 if (dref->freq_base != ref->freq_base)
207 rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE,
208 ref->freq_base);
209 if (!rc && dref->freq_mult != ref->freq_mult)
210 rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT,
211 ref->freq_mult);
212 if (!rc && dref->freq_ratio_m != ref->freq_ratio_m)
213 rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M,
214 ref->freq_ratio_m);
215 if (!rc && dref->freq_ratio_n != ref->freq_ratio_n)
216 rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N,
217 ref->freq_ratio_n);
218 if (!rc && dref->esync_n_div != ref->esync_n_div)
219 rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
220 ref->esync_n_div);
221 if (!rc && dref->sync_ctrl != ref->sync_ctrl)
222 rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
223 ref->sync_ctrl);
224 if (!rc && dref->phase_comp != ref->phase_comp) {
225 if (zl3073x_dev_is_ref_phase_comp_32bit(zldev))
226 rc = zl3073x_write_u32(zldev,
227 ZL_REG_REF_PHASE_OFFSET_COMP_32,
228 ref->phase_comp);
229 else
230 rc = zl3073x_write_u48(zldev,
231 ZL_REG_REF_PHASE_OFFSET_COMP,
232 ref->phase_comp);
233 }
234 if (rc)
235 return rc;
236
237 /* Commit reference configuration */
238 rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
239 ZL_REG_REF_MB_MASK, BIT(index));
240 if (rc)
241 return rc;
242
243 /* After successful commit store new state */
244 dref->cfg = ref->cfg;
245
246 return 0;
247 }
248