xref: /linux/drivers/dpll/zl3073x/ref.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
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 	rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
125 			      &ref->phase_comp);
126 	if (rc)
127 		return rc;
128 
129 	dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
130 		str_enabled_disabled(zl3073x_ref_is_enabled(ref)),
131 		zl3073x_ref_is_diff(ref) ? "differential" : "single-ended");
132 
133 	return rc;
134 }
135 
136 /**
137  * zl3073x_ref_state_get - get current input reference state
138  * @zldev: pointer to zl3073x_dev structure
139  * @index: input reference index to get state for
140  *
141  * Return: pointer to given input reference state
142  */
143 const struct zl3073x_ref *
144 zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
145 {
146 	return &zldev->ref[index];
147 }
148 
149 int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
150 			  const struct zl3073x_ref *ref)
151 {
152 	struct zl3073x_ref *dref = &zldev->ref[index];
153 	int rc;
154 
155 	guard(mutex)(&zldev->multiop_lock);
156 
157 	/* Read reference configuration into mailbox */
158 	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
159 			   ZL_REG_REF_MB_MASK, BIT(index));
160 	if (rc)
161 		return rc;
162 
163 	/* Update mailbox with changed values */
164 	if (dref->freq_base != ref->freq_base)
165 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE,
166 				       ref->freq_base);
167 	if (!rc && dref->freq_mult != ref->freq_mult)
168 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT,
169 				       ref->freq_mult);
170 	if (!rc && dref->freq_ratio_m != ref->freq_ratio_m)
171 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M,
172 				       ref->freq_ratio_m);
173 	if (!rc && dref->freq_ratio_n != ref->freq_ratio_n)
174 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N,
175 				       ref->freq_ratio_n);
176 	if (!rc && dref->esync_n_div != ref->esync_n_div)
177 		rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
178 				       ref->esync_n_div);
179 	if (!rc && dref->sync_ctrl != ref->sync_ctrl)
180 		rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
181 				      ref->sync_ctrl);
182 	if (!rc && dref->phase_comp != ref->phase_comp)
183 		rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
184 				       ref->phase_comp);
185 	if (rc)
186 		return rc;
187 
188 	/* Commit reference configuration */
189 	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
190 			   ZL_REG_REF_MB_MASK, BIT(index));
191 	if (rc)
192 		return rc;
193 
194 	/* After successful commit store new state */
195 	dref->freq_base = ref->freq_base;
196 	dref->freq_mult = ref->freq_mult;
197 	dref->freq_ratio_m = ref->freq_ratio_m;
198 	dref->freq_ratio_n = ref->freq_ratio_n;
199 	dref->esync_n_div = ref->esync_n_div;
200 	dref->sync_ctrl = ref->sync_ctrl;
201 	dref->phase_comp = ref->phase_comp;
202 
203 	return 0;
204 }
205