xref: /linux/drivers/dpll/zl3073x/ref.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
1607f2c00SIvan Vecera // SPDX-License-Identifier: GPL-2.0-only
2607f2c00SIvan Vecera 
3607f2c00SIvan Vecera #include <linux/bitfield.h>
4607f2c00SIvan Vecera #include <linux/cleanup.h>
5607f2c00SIvan Vecera #include <linux/dev_printk.h>
6607f2c00SIvan Vecera #include <linux/string.h>
7607f2c00SIvan Vecera #include <linux/string_choices.h>
8607f2c00SIvan Vecera #include <linux/types.h>
9607f2c00SIvan Vecera 
10607f2c00SIvan Vecera #include "core.h"
11607f2c00SIvan Vecera #include "ref.h"
12607f2c00SIvan Vecera 
13607f2c00SIvan Vecera /**
14607f2c00SIvan Vecera  * zl3073x_ref_freq_factorize - factorize given frequency
15607f2c00SIvan Vecera  * @freq: input frequency
16607f2c00SIvan Vecera  * @base: base frequency
17607f2c00SIvan Vecera  * @mult: multiplier
18607f2c00SIvan Vecera  *
19607f2c00SIvan Vecera  * Checks if the given frequency can be factorized using one of the
20607f2c00SIvan Vecera  * supported base frequencies. If so the base frequency and multiplier
21607f2c00SIvan Vecera  * are stored into appropriate parameters if they are not NULL.
22607f2c00SIvan Vecera  *
23607f2c00SIvan Vecera  * Return: 0 on success, -EINVAL if the frequency cannot be factorized
24607f2c00SIvan Vecera  */
25607f2c00SIvan Vecera int
26607f2c00SIvan Vecera zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
27607f2c00SIvan Vecera {
28607f2c00SIvan Vecera 	static const u16 base_freqs[] = {
29607f2c00SIvan Vecera 		1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125,
30607f2c00SIvan Vecera 		128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000,
31607f2c00SIvan Vecera 		1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250,
32607f2c00SIvan Vecera 		6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250,
33607f2c00SIvan Vecera 		32000, 40000, 50000, 62500,
34607f2c00SIvan Vecera 	};
35607f2c00SIvan Vecera 	u32 div;
36607f2c00SIvan Vecera 	int i;
37607f2c00SIvan Vecera 
38607f2c00SIvan Vecera 	for (i = 0; i < ARRAY_SIZE(base_freqs); i++) {
39607f2c00SIvan Vecera 		div = freq / base_freqs[i];
40607f2c00SIvan Vecera 
41607f2c00SIvan Vecera 		if (div <= U16_MAX && (freq % base_freqs[i]) == 0) {
42607f2c00SIvan Vecera 			if (base)
43607f2c00SIvan Vecera 				*base = base_freqs[i];
44607f2c00SIvan Vecera 			if (mult)
45607f2c00SIvan Vecera 				*mult = div;
46607f2c00SIvan Vecera 
47607f2c00SIvan Vecera 			return 0;
48607f2c00SIvan Vecera 		}
49607f2c00SIvan Vecera 	}
50607f2c00SIvan Vecera 
51607f2c00SIvan Vecera 	return -EINVAL;
52607f2c00SIvan Vecera }
53607f2c00SIvan Vecera 
54607f2c00SIvan Vecera /**
55607f2c00SIvan Vecera  * zl3073x_ref_state_fetch - fetch input reference state from hardware
56607f2c00SIvan Vecera  * @zldev: pointer to zl3073x_dev structure
57607f2c00SIvan Vecera  * @index: input reference index to fetch state for
58607f2c00SIvan Vecera  *
59607f2c00SIvan Vecera  * Function fetches state for the given input reference from hardware and
60607f2c00SIvan Vecera  * stores it for later use.
61607f2c00SIvan Vecera  *
62607f2c00SIvan Vecera  * Return: 0 on success, <0 on error
63607f2c00SIvan Vecera  */
64607f2c00SIvan Vecera int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
65607f2c00SIvan Vecera {
66607f2c00SIvan Vecera 	struct zl3073x_ref *ref = &zldev->ref[index];
67607f2c00SIvan Vecera 	int rc;
68607f2c00SIvan Vecera 
69607f2c00SIvan Vecera 	/* For differential type inputs the N-pin reference shares
70607f2c00SIvan Vecera 	 * part of the configuration with the P-pin counterpart.
71607f2c00SIvan Vecera 	 */
72607f2c00SIvan Vecera 	if (zl3073x_is_n_pin(index) && zl3073x_ref_is_diff(ref - 1)) {
73*5bc02b19SIvan Vecera 		struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
74607f2c00SIvan Vecera 
75607f2c00SIvan Vecera 		/* Copy the shared items from the P-pin */
76607f2c00SIvan Vecera 		ref->config = p_ref->config;
77*5bc02b19SIvan Vecera 		ref->esync_n_div = p_ref->esync_n_div;
78*5bc02b19SIvan Vecera 		ref->freq_base = p_ref->freq_base;
79*5bc02b19SIvan Vecera 		ref->freq_mult = p_ref->freq_mult;
80*5bc02b19SIvan Vecera 		ref->freq_ratio_m = p_ref->freq_ratio_m;
81*5bc02b19SIvan Vecera 		ref->freq_ratio_n = p_ref->freq_ratio_n;
82*5bc02b19SIvan Vecera 		ref->phase_comp = p_ref->phase_comp;
83*5bc02b19SIvan Vecera 		ref->sync_ctrl = p_ref->sync_ctrl;
84607f2c00SIvan Vecera 
85607f2c00SIvan Vecera 		return 0; /* Finish - no non-shared items for now */
86607f2c00SIvan Vecera 	}
87607f2c00SIvan Vecera 
88607f2c00SIvan Vecera 	guard(mutex)(&zldev->multiop_lock);
89607f2c00SIvan Vecera 
90607f2c00SIvan Vecera 	/* Read reference configuration */
91607f2c00SIvan Vecera 	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
92607f2c00SIvan Vecera 			   ZL_REG_REF_MB_MASK, BIT(index));
93607f2c00SIvan Vecera 	if (rc)
94607f2c00SIvan Vecera 		return rc;
95607f2c00SIvan Vecera 
96607f2c00SIvan Vecera 	/* Read ref_config register */
97607f2c00SIvan Vecera 	rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref->config);
98607f2c00SIvan Vecera 	if (rc)
99607f2c00SIvan Vecera 		return rc;
100607f2c00SIvan Vecera 
101*5bc02b19SIvan Vecera 	/* Read frequency related registers */
102*5bc02b19SIvan Vecera 	rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &ref->freq_base);
103*5bc02b19SIvan Vecera 	if (rc)
104*5bc02b19SIvan Vecera 		return rc;
105*5bc02b19SIvan Vecera 	rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &ref->freq_mult);
106*5bc02b19SIvan Vecera 	if (rc)
107*5bc02b19SIvan Vecera 		return rc;
108*5bc02b19SIvan Vecera 	rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &ref->freq_ratio_m);
109*5bc02b19SIvan Vecera 	if (rc)
110*5bc02b19SIvan Vecera 		return rc;
111*5bc02b19SIvan Vecera 	rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &ref->freq_ratio_n);
112*5bc02b19SIvan Vecera 	if (rc)
113*5bc02b19SIvan Vecera 		return rc;
114*5bc02b19SIvan Vecera 
115*5bc02b19SIvan Vecera 	/* Read eSync and N-div rated registers */
116*5bc02b19SIvan Vecera 	rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &ref->esync_n_div);
117*5bc02b19SIvan Vecera 	if (rc)
118*5bc02b19SIvan Vecera 		return rc;
119*5bc02b19SIvan Vecera 	rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref->sync_ctrl);
120*5bc02b19SIvan Vecera 	if (rc)
121*5bc02b19SIvan Vecera 		return rc;
122*5bc02b19SIvan Vecera 
123*5bc02b19SIvan Vecera 	/* Read phase compensation register */
124*5bc02b19SIvan Vecera 	rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
125*5bc02b19SIvan Vecera 			      &ref->phase_comp);
126*5bc02b19SIvan Vecera 	if (rc)
127*5bc02b19SIvan Vecera 		return rc;
128*5bc02b19SIvan Vecera 
129607f2c00SIvan Vecera 	dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
130607f2c00SIvan Vecera 		str_enabled_disabled(zl3073x_ref_is_enabled(ref)),
131607f2c00SIvan Vecera 		zl3073x_ref_is_diff(ref) ? "differential" : "single-ended");
132607f2c00SIvan Vecera 
133607f2c00SIvan Vecera 	return rc;
134607f2c00SIvan Vecera }
135607f2c00SIvan Vecera 
136607f2c00SIvan Vecera /**
137607f2c00SIvan Vecera  * zl3073x_ref_state_get - get current input reference state
138607f2c00SIvan Vecera  * @zldev: pointer to zl3073x_dev structure
139607f2c00SIvan Vecera  * @index: input reference index to get state for
140607f2c00SIvan Vecera  *
141607f2c00SIvan Vecera  * Return: pointer to given input reference state
142607f2c00SIvan Vecera  */
143607f2c00SIvan Vecera const struct zl3073x_ref *
144607f2c00SIvan Vecera zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
145607f2c00SIvan Vecera {
146607f2c00SIvan Vecera 	return &zldev->ref[index];
147607f2c00SIvan Vecera }
148*5bc02b19SIvan Vecera 
149*5bc02b19SIvan Vecera int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
150*5bc02b19SIvan Vecera 			  const struct zl3073x_ref *ref)
151*5bc02b19SIvan Vecera {
152*5bc02b19SIvan Vecera 	struct zl3073x_ref *dref = &zldev->ref[index];
153*5bc02b19SIvan Vecera 	int rc;
154*5bc02b19SIvan Vecera 
155*5bc02b19SIvan Vecera 	guard(mutex)(&zldev->multiop_lock);
156*5bc02b19SIvan Vecera 
157*5bc02b19SIvan Vecera 	/* Read reference configuration into mailbox */
158*5bc02b19SIvan Vecera 	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
159*5bc02b19SIvan Vecera 			   ZL_REG_REF_MB_MASK, BIT(index));
160*5bc02b19SIvan Vecera 	if (rc)
161*5bc02b19SIvan Vecera 		return rc;
162*5bc02b19SIvan Vecera 
163*5bc02b19SIvan Vecera 	/* Update mailbox with changed values */
164*5bc02b19SIvan Vecera 	if (dref->freq_base != ref->freq_base)
165*5bc02b19SIvan Vecera 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE,
166*5bc02b19SIvan Vecera 				       ref->freq_base);
167*5bc02b19SIvan Vecera 	if (!rc && dref->freq_mult != ref->freq_mult)
168*5bc02b19SIvan Vecera 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT,
169*5bc02b19SIvan Vecera 				       ref->freq_mult);
170*5bc02b19SIvan Vecera 	if (!rc && dref->freq_ratio_m != ref->freq_ratio_m)
171*5bc02b19SIvan Vecera 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M,
172*5bc02b19SIvan Vecera 				       ref->freq_ratio_m);
173*5bc02b19SIvan Vecera 	if (!rc && dref->freq_ratio_n != ref->freq_ratio_n)
174*5bc02b19SIvan Vecera 		rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N,
175*5bc02b19SIvan Vecera 				       ref->freq_ratio_n);
176*5bc02b19SIvan Vecera 	if (!rc && dref->esync_n_div != ref->esync_n_div)
177*5bc02b19SIvan Vecera 		rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
178*5bc02b19SIvan Vecera 				       ref->esync_n_div);
179*5bc02b19SIvan Vecera 	if (!rc && dref->sync_ctrl != ref->sync_ctrl)
180*5bc02b19SIvan Vecera 		rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
181*5bc02b19SIvan Vecera 				      ref->sync_ctrl);
182*5bc02b19SIvan Vecera 	if (!rc && dref->phase_comp != ref->phase_comp)
183*5bc02b19SIvan Vecera 		rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
184*5bc02b19SIvan Vecera 				       ref->phase_comp);
185*5bc02b19SIvan Vecera 	if (rc)
186*5bc02b19SIvan Vecera 		return rc;
187*5bc02b19SIvan Vecera 
188*5bc02b19SIvan Vecera 	/* Commit reference configuration */
189*5bc02b19SIvan Vecera 	rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
190*5bc02b19SIvan Vecera 			   ZL_REG_REF_MB_MASK, BIT(index));
191*5bc02b19SIvan Vecera 	if (rc)
192*5bc02b19SIvan Vecera 		return rc;
193*5bc02b19SIvan Vecera 
194*5bc02b19SIvan Vecera 	/* After successful commit store new state */
195*5bc02b19SIvan Vecera 	dref->freq_base = ref->freq_base;
196*5bc02b19SIvan Vecera 	dref->freq_mult = ref->freq_mult;
197*5bc02b19SIvan Vecera 	dref->freq_ratio_m = ref->freq_ratio_m;
198*5bc02b19SIvan Vecera 	dref->freq_ratio_n = ref->freq_ratio_n;
199*5bc02b19SIvan Vecera 	dref->esync_n_div = ref->esync_n_div;
200*5bc02b19SIvan Vecera 	dref->sync_ctrl = ref->sync_ctrl;
201*5bc02b19SIvan Vecera 	dref->phase_comp = ref->phase_comp;
202*5bc02b19SIvan Vecera 
203*5bc02b19SIvan Vecera 	return 0;
204*5bc02b19SIvan Vecera }
205