xref: /linux/drivers/dpll/zl3073x/prop.c (revision 456027325c9cd2be94229687f2c412ae6f351bd8)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <linux/array_size.h>
4 #include <linux/dev_printk.h>
5 #include <linux/err.h>
6 #include <linux/errno.h>
7 #include <linux/fwnode.h>
8 #include <linux/property.h>
9 #include <linux/slab.h>
10 #include <linux/string.h>
11 
12 #include "core.h"
13 #include "prop.h"
14 
15 /**
16  * zl3073x_pin_check_freq - verify frequency for given pin
17  * @zldev: pointer to zl3073x device
18  * @dir: pin direction
19  * @id: pin index
20  * @freq: frequency to check
21  *
22  * The function checks the given frequency is valid for the device. For input
23  * pins it checks that the frequency can be factorized using supported base
24  * frequencies. For output pins it checks that the frequency divides connected
25  * synth frequency without remainder and that the resulting divisor is not
26  * below the minimum the driver supports.
27  *
28  * Return: true if the frequency is valid, false if not.
29  */
30 static bool
31 zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir,
32 		       u8 id, u64 freq)
33 {
34 	if (!freq || freq > U32_MAX)
35 		goto err_inv_freq;
36 
37 	if (dir == DPLL_PIN_DIRECTION_INPUT) {
38 		int rc;
39 
40 		/* Check if the frequency can be factorized */
41 		rc = zl3073x_ref_freq_factorize(freq, NULL, NULL);
42 		if (rc)
43 			goto err_inv_freq;
44 	} else {
45 		u32 synth_freq, div, min_div, rem;
46 		const struct zl3073x_out *out;
47 		u8 out_id, synth;
48 
49 		/* Get output pin synthesizer */
50 		out_id = zl3073x_output_pin_out_get(id);
51 		synth = zl3073x_dev_out_synth_get(zldev, out_id);
52 
53 		/* Get synth frequency */
54 		synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
55 
56 		/* Check the frequency divides synth frequency */
57 		div = div_u64_rem(synth_freq, freq, &rem);
58 		if (rem)
59 			goto err_inv_freq;
60 
61 		/* The driver requires an output divisor of at least 2 -
62 		 * below that the hardware bypasses the divider and ignores
63 		 * the pulse width and eSync registers the driver programs.
64 		 * For the N-pin of an N-divided output the effective divisor
65 		 * also includes the N divider (>= 2), so the minimum is 4.
66 		 * For N-divided outputs this is only a necessary condition -
67 		 * whether the frequency can be set also depends on the output
68 		 * divisor shared by both pins, which is checked when the
69 		 * frequency is set.
70 		 */
71 		out = zl3073x_out_state_get(zldev, out_id);
72 		if (zl3073x_out_is_ndiv(out) && zl3073x_is_n_pin(id))
73 			min_div = 4;
74 		else
75 			min_div = 2;
76 		if (div < min_div)
77 			goto err_inv_freq;
78 	}
79 
80 	return true;
81 
82 err_inv_freq:
83 	dev_warn(zldev->dev,
84 		 "Unsupported frequency %llu Hz in firmware node\n", freq);
85 
86 	return false;
87 }
88 
89 /**
90  * zl3073x_prop_pin_package_label_set - get package label for the pin
91  * @zldev: pointer to zl3073x device
92  * @props: pointer to pin properties
93  * @dir: pin direction
94  * @id: pin index
95  *
96  * Generates package label string and stores it into pin properties structure.
97  *
98  * Possible formats:
99  * REF<n> - differential input reference
100  * REF<n>P & REF<n>N - single-ended input reference (P or N pin)
101  * OUT<n> - differential output
102  * OUT<n>P & OUT<n>N - single-ended output (P or N pin)
103  */
104 static void
105 zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev,
106 				   struct zl3073x_pin_props *props,
107 				   enum dpll_pin_direction dir, u8 id)
108 {
109 	const char *prefix, *suffix;
110 	bool is_diff;
111 
112 	if (dir == DPLL_PIN_DIRECTION_INPUT) {
113 		u8 ref;
114 
115 		prefix = "REF";
116 		ref = zl3073x_input_pin_ref_get(id);
117 		is_diff = zl3073x_dev_ref_is_diff(zldev, ref);
118 	} else {
119 		u8 out;
120 
121 		prefix = "OUT";
122 		out = zl3073x_output_pin_out_get(id);
123 		is_diff = zl3073x_dev_out_is_diff(zldev, out);
124 	}
125 
126 	if (!is_diff)
127 		suffix = zl3073x_is_p_pin(id) ? "P" : "N";
128 	else
129 		suffix = ""; /* No suffix for differential one */
130 
131 	snprintf(props->package_label, sizeof(props->package_label), "%s%u%s",
132 		 prefix, id / 2, suffix);
133 
134 	/* Set package_label pointer in DPLL core properties to generated
135 	 * string.
136 	 */
137 	props->dpll_props.package_label = props->package_label;
138 }
139 
140 /**
141  * zl3073x_prop_pin_fwnode_get - get fwnode for given pin
142  * @zldev: pointer to zl3073x device
143  * @props: pointer to pin properties
144  * @dir: pin direction
145  * @id: pin index
146  *
147  * Return: 0 on success, -ENOENT if the firmware node does not exist
148  */
149 static int
150 zl3073x_prop_pin_fwnode_get(struct zl3073x_dev *zldev,
151 			    struct zl3073x_pin_props *props,
152 			    enum dpll_pin_direction dir, u8 id)
153 {
154 	struct fwnode_handle *pins_node, *pin_node;
155 	const char *node_name;
156 
157 	if (dir == DPLL_PIN_DIRECTION_INPUT)
158 		node_name = "input-pins";
159 	else
160 		node_name = "output-pins";
161 
162 	/* Get node containing input or output pins */
163 	pins_node = device_get_named_child_node(zldev->dev, node_name);
164 	if (!pins_node) {
165 		dev_dbg(zldev->dev, "'%s' sub-node is missing\n", node_name);
166 		return -ENOENT;
167 	}
168 
169 	/* Enumerate child pin nodes and find the requested one */
170 	fwnode_for_each_child_node(pins_node, pin_node) {
171 		u32 reg;
172 
173 		if (fwnode_property_read_u32(pin_node, "reg", &reg))
174 			continue;
175 
176 		if (id == reg)
177 			break;
178 	}
179 
180 	/* Release pin parent node */
181 	fwnode_handle_put(pins_node);
182 
183 	/* Save found node */
184 	props->fwnode = pin_node;
185 
186 	dev_dbg(zldev->dev, "Firmware node for %s %sfound\n",
187 		props->package_label, pin_node ? "" : "NOT ");
188 
189 	return pin_node ? 0 : -ENOENT;
190 }
191 
192 /**
193  * zl3073x_pin_props_get - get pin properties
194  * @zldev: pointer to zl3073x device
195  * @dir: pin direction
196  * @index: pin index
197  *
198  * The function looks for firmware node for the given pin if it is provided
199  * by the system firmware (DT or ACPI), allocates pin properties structure,
200  * generates package label string according pin type and optionally fetches
201  * board label, connection type, supported frequencies and esync capability
202  * from the firmware node if it does exist.
203  *
204  * Pointer that is returned by this function should be freed using
205  * @zl3073x_pin_props_put().
206  *
207  * Return:
208  * * pointer to allocated pin properties structure on success
209  * * error pointer in case of error
210  */
211 struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
212 						enum dpll_pin_direction dir,
213 						u8 index)
214 {
215 	struct dpll_pin_frequency *ranges;
216 	struct zl3073x_pin_props *props;
217 	int i, j, num_freqs = 0, rc;
218 	u64 *freqs = NULL;
219 	const char *type;
220 	u32 curr_freq;
221 
222 	props = kzalloc_obj(*props);
223 	if (!props)
224 		return ERR_PTR(-ENOMEM);
225 
226 	/* Set default pin type and capabilities */
227 	if (dir == DPLL_PIN_DIRECTION_INPUT) {
228 		props->dpll_props.type = DPLL_PIN_TYPE_EXT;
229 		props->dpll_props.capabilities =
230 			DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
231 			DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
232 		curr_freq = zl3073x_dev_ref_freq_get(zldev, index);
233 	} else {
234 		u8 out, synth;
235 		u32 f;
236 
237 		props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
238 
239 		/* The output pin phase adjustment granularity equals half of
240 		 * the synth frequency count.
241 		 */
242 		out = zl3073x_output_pin_out_get(index);
243 		synth = zl3073x_dev_out_synth_get(zldev, out);
244 		f = 2 * zl3073x_dev_synth_freq_get(zldev, synth);
245 		props->dpll_props.phase_gran = f ? div_u64(PSEC_PER_SEC, f) : 1;
246 		curr_freq = zl3073x_dev_output_pin_freq_get(zldev, index);
247 	}
248 
249 	props->dpll_props.phase_range.min = S32_MIN;
250 	props->dpll_props.phase_range.max = S32_MAX;
251 
252 	zl3073x_prop_pin_package_label_set(zldev, props, dir, index);
253 
254 	/* Get firmware node for the given pin */
255 	rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index);
256 	if (rc)
257 		goto skip_fwnode_props;
258 
259 	/* Look for label property and store the value as board label */
260 	fwnode_property_read_string(props->fwnode, "label",
261 				    &props->dpll_props.board_label);
262 
263 	/* Look for pin type property and translate its value to DPLL
264 	 * pin type enum if it is present.
265 	 */
266 	if (!fwnode_property_read_string(props->fwnode, "connection-type",
267 					 &type)) {
268 		if (!strcmp(type, "ext"))
269 			props->dpll_props.type = DPLL_PIN_TYPE_EXT;
270 		else if (!strcmp(type, "gnss"))
271 			props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
272 		else if (!strcmp(type, "int"))
273 			props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
274 		else if (!strcmp(type, "synce"))
275 			props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
276 		else if (!strcmp(type, "mux"))
277 			props->dpll_props.type = DPLL_PIN_TYPE_MUX;
278 		else
279 			dev_warn(zldev->dev,
280 				 "Unknown or unsupported pin type '%s'\n",
281 				 type);
282 	}
283 
284 	/* Check if the pin supports embedded sync control */
285 	props->esync_control = fwnode_property_read_bool(props->fwnode,
286 							 "esync-control");
287 
288 	/* Read supported frequencies property if it is specified */
289 	num_freqs = fwnode_property_count_u64(props->fwnode,
290 					      "supported-frequencies-hz");
291 	if (num_freqs <= 0) {
292 		num_freqs = 0;
293 		goto skip_fwnode_props;
294 	}
295 
296 	/* The firmware node specifies list of supported frequencies while
297 	 * DPLL core pin properties requires list of frequency ranges.
298 	 * So read the frequency list into temporary array.
299 	 */
300 	freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL);
301 	if (!freqs) {
302 		rc = -ENOMEM;
303 		goto err_alloc_freqs;
304 	}
305 
306 	/* Read frequencies list from firmware node */
307 	fwnode_property_read_u64_array(props->fwnode,
308 				       "supported-frequencies-hz", freqs,
309 				       num_freqs);
310 
311 skip_fwnode_props:
312 	/* Allocate frequency ranges list - extra slot for current frequency */
313 	ranges = kzalloc_objs(*ranges, num_freqs + 1);
314 	if (!ranges) {
315 		rc = -ENOMEM;
316 		goto err_alloc_ranges;
317 	}
318 
319 	/* Start with current frequency at index 0. A sub-Hz frequency is
320 	 * read back as 0 Hz and cannot be set, so it is not offered.
321 	 */
322 	j = 0;
323 	if (curr_freq) {
324 		struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(curr_freq);
325 
326 		ranges[j++] = freq;
327 	}
328 
329 	/* Add frequencies from firmware node, skipping current frequency
330 	 * and filtering out frequencies not representable by device
331 	 */
332 	for (i = 0; i < num_freqs; i++) {
333 		struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]);
334 
335 		if (freqs[i] == curr_freq)
336 			continue;
337 		if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) {
338 			ranges[j] = freq;
339 			j++;
340 		}
341 	}
342 
343 	/* The DPLL core rejects a non-NULL list without entries, so do
344 	 * not publish the array at all when nothing is settable.
345 	 */
346 	if (!j) {
347 		kfree(ranges);
348 		ranges = NULL;
349 	}
350 
351 	/* Save number of freq ranges and pointer to them into pin properties */
352 	props->dpll_props.freq_supported = ranges;
353 	props->dpll_props.freq_supported_num = j;
354 
355 	/* Free temporary array */
356 	kfree(freqs);
357 
358 	return props;
359 
360 err_alloc_ranges:
361 	kfree(freqs);
362 err_alloc_freqs:
363 	fwnode_handle_put(props->fwnode);
364 	kfree(props);
365 
366 	return ERR_PTR(rc);
367 }
368 
369 /**
370  * zl3073x_pin_props_put - release pin properties
371  * @props: pin properties to free
372  *
373  * The function deallocates given pin properties structure.
374  */
375 void zl3073x_pin_props_put(struct zl3073x_pin_props *props)
376 {
377 	/* Free supported frequency ranges list if it is present */
378 	kfree(props->dpll_props.freq_supported);
379 
380 	/* Put firmware handle if it is present */
381 	if (props->fwnode)
382 		fwnode_handle_put(props->fwnode);
383 
384 	kfree(props);
385 }
386 
387 /**
388  * zl3073x_prop_dpll_type_get - get DPLL channel type
389  * @zldev: pointer to zl3073x device
390  * @index: DPLL channel index
391  *
392  * Return: DPLL type for given DPLL channel
393  */
394 enum dpll_type
395 zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index)
396 {
397 	const char *types[ZL3073X_MAX_CHANNELS];
398 	int count;
399 
400 	/* Read dpll types property from firmware */
401 	count = device_property_read_string_array(zldev->dev, "dpll-types",
402 						  types, ARRAY_SIZE(types));
403 
404 	/* Return default if property or entry for given channel is missing */
405 	if (index >= count)
406 		return DPLL_TYPE_PPS;
407 
408 	if (!strcmp(types[index], "pps"))
409 		return DPLL_TYPE_PPS;
410 	else if (!strcmp(types[index], "eec"))
411 		return DPLL_TYPE_EEC;
412 
413 	dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n",
414 		 types[index]);
415 
416 	return DPLL_TYPE_PPS; /* Default */
417 }
418