File Index
Files and directories
Sign in
FILE

prop.c

File preview with syntax highlighting.

linux-6.19.14/drivers/dpll/zl3073x/prop.c C
  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.
 26 *
 27 * Return: true if the frequency is valid, false if not.
 28 */
 29static bool
 30zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir,
 31		       u8 id, u64 freq)
 32{
 33	if (freq > U32_MAX)
 34		goto err_inv_freq;
 35
 36	if (dir == DPLL_PIN_DIRECTION_INPUT) {
 37		int rc;
 38
 39		/* Check if the frequency can be factorized */
 40		rc = zl3073x_ref_freq_factorize(freq, NULL, NULL);
 41		if (rc)
 42			goto err_inv_freq;
 43	} else {
 44		u32 synth_freq;
 45		u8 out, synth;
 46
 47		/* Get output pin synthesizer */
 48		out = zl3073x_output_pin_out_get(id);
 49		synth = zl3073x_dev_out_synth_get(zldev, out);
 50
 51		/* Get synth frequency */
 52		synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
 53
 54		/* Check the frequency divides synth frequency */
 55		if (synth_freq % (u32)freq)
 56			goto err_inv_freq;
 57	}
 58
 59	return true;
 60
 61err_inv_freq:
 62	dev_warn(zldev->dev,
 63		 "Unsupported frequency %llu Hz in firmware node\n", freq);
 64
 65	return false;
 66}
 67
 68/**
 69 * zl3073x_prop_pin_package_label_set - get package label for the pin
 70 * @zldev: pointer to zl3073x device
 71 * @props: pointer to pin properties
 72 * @dir: pin direction
 73 * @id: pin index
 74 *
 75 * Generates package label string and stores it into pin properties structure.
 76 *
 77 * Possible formats:
 78 * REF<n> - differential input reference
 79 * REF<n>P & REF<n>N - single-ended input reference (P or N pin)
 80 * OUT<n> - differential output
 81 * OUT<n>P & OUT<n>N - single-ended output (P or N pin)
 82 */
 83static void
 84zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev,
 85				   struct zl3073x_pin_props *props,
 86				   enum dpll_pin_direction dir, u8 id)
 87{
 88	const char *prefix, *suffix;
 89	bool is_diff;
 90
 91	if (dir == DPLL_PIN_DIRECTION_INPUT) {
 92		u8 ref;
 93
 94		prefix = "REF";
 95		ref = zl3073x_input_pin_ref_get(id);
 96		is_diff = zl3073x_dev_ref_is_diff(zldev, ref);
 97	} else {
 98		u8 out;
 99
100		prefix = "OUT";
101		out = zl3073x_output_pin_out_get(id);
102		is_diff = zl3073x_dev_out_is_diff(zldev, out);
103	}
104
105	if (!is_diff)
106		suffix = zl3073x_is_p_pin(id) ? "P" : "N";
107	else
108		suffix = ""; /* No suffix for differential one */
109
110	snprintf(props->package_label, sizeof(props->package_label), "%s%u%s",
111		 prefix, id / 2, suffix);
112
113	/* Set package_label pointer in DPLL core properties to generated
114	 * string.
115	 */
116	props->dpll_props.package_label = props->package_label;
117}
118
119/**
120 * zl3073x_prop_pin_fwnode_get - get fwnode for given pin
121 * @zldev: pointer to zl3073x device
122 * @props: pointer to pin properties
123 * @dir: pin direction
124 * @id: pin index
125 *
126 * Return: 0 on success, -ENOENT if the firmware node does not exist
127 */
128static int
129zl3073x_prop_pin_fwnode_get(struct zl3073x_dev *zldev,
130			    struct zl3073x_pin_props *props,
131			    enum dpll_pin_direction dir, u8 id)
132{
133	struct fwnode_handle *pins_node, *pin_node;
134	const char *node_name;
135
136	if (dir == DPLL_PIN_DIRECTION_INPUT)
137		node_name = "input-pins";
138	else
139		node_name = "output-pins";
140
141	/* Get node containing input or output pins */
142	pins_node = device_get_named_child_node(zldev->dev, node_name);
143	if (!pins_node) {
144		dev_dbg(zldev->dev, "'%s' sub-node is missing\n", node_name);
145		return -ENOENT;
146	}
147
148	/* Enumerate child pin nodes and find the requested one */
149	fwnode_for_each_child_node(pins_node, pin_node) {
150		u32 reg;
151
152		if (fwnode_property_read_u32(pin_node, "reg", &reg))
153			continue;
154
155		if (id == reg)
156			break;
157	}
158
159	/* Release pin parent node */
160	fwnode_handle_put(pins_node);
161
162	/* Save found node */
163	props->fwnode = pin_node;
164
165	dev_dbg(zldev->dev, "Firmware node for %s %sfound\n",
166		props->package_label, pin_node ? "" : "NOT ");
167
168	return pin_node ? 0 : -ENOENT;
169}
170
171/**
172 * zl3073x_pin_props_get - get pin properties
173 * @zldev: pointer to zl3073x device
174 * @dir: pin direction
175 * @index: pin index
176 *
177 * The function looks for firmware node for the given pin if it is provided
178 * by the system firmware (DT or ACPI), allocates pin properties structure,
179 * generates package label string according pin type and optionally fetches
180 * board label, connection type, supported frequencies and esync capability
181 * from the firmware node if it does exist.
182 *
183 * Pointer that is returned by this function should be freed using
184 * @zl3073x_pin_props_put().
185 *
186 * Return:
187 * * pointer to allocated pin properties structure on success
188 * * error pointer in case of error
189 */
190struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
191						enum dpll_pin_direction dir,
192						u8 index)
193{
194	struct dpll_pin_frequency *ranges;
195	struct zl3073x_pin_props *props;
196	int i, j, num_freqs, rc;
197	const char *type;
198	u64 *freqs;
199
200	props = kzalloc(sizeof(*props), GFP_KERNEL);
201	if (!props)
202		return ERR_PTR(-ENOMEM);
203
204	/* Set default pin type and capabilities */
205	if (dir == DPLL_PIN_DIRECTION_INPUT) {
206		props->dpll_props.type = DPLL_PIN_TYPE_EXT;
207		props->dpll_props.capabilities =
208			DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
209			DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
210	} else {
211		u8 out, synth;
212		u32 f;
213
214		props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
215
216		/* The output pin phase adjustment granularity equals half of
217		 * the synth frequency count.
218		 */
219		out = zl3073x_output_pin_out_get(index);
220		synth = zl3073x_dev_out_synth_get(zldev, out);
221		f = 2 * zl3073x_dev_synth_freq_get(zldev, synth);
222		props->dpll_props.phase_gran = f ? div_u64(PSEC_PER_SEC, f) : 1;
223	}
224
225	props->dpll_props.phase_range.min = S32_MIN;
226	props->dpll_props.phase_range.max = S32_MAX;
227
228	zl3073x_prop_pin_package_label_set(zldev, props, dir, index);
229
230	/* Get firmware node for the given pin */
231	rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index);
232	if (rc)
233		return props; /* Return if it does not exist */
234
235	/* Look for label property and store the value as board label */
236	fwnode_property_read_string(props->fwnode, "label",
237				    &props->dpll_props.board_label);
238
239	/* Look for pin type property and translate its value to DPLL
240	 * pin type enum if it is present.
241	 */
242	if (!fwnode_property_read_string(props->fwnode, "connection-type",
243					 &type)) {
244		if (!strcmp(type, "ext"))
245			props->dpll_props.type = DPLL_PIN_TYPE_EXT;
246		else if (!strcmp(type, "gnss"))
247			props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
248		else if (!strcmp(type, "int"))
249			props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
250		else if (!strcmp(type, "synce"))
251			props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
252		else
253			dev_warn(zldev->dev,
254				 "Unknown or unsupported pin type '%s'\n",
255				 type);
256	}
257
258	/* Check if the pin supports embedded sync control */
259	props->esync_control = fwnode_property_read_bool(props->fwnode,
260							 "esync-control");
261
262	/* Read supported frequencies property if it is specified */
263	num_freqs = fwnode_property_count_u64(props->fwnode,
264					      "supported-frequencies-hz");
265	if (num_freqs <= 0)
266		/* Return if the property does not exist or number is 0 */
267		return props;
268
269	/* The firmware node specifies list of supported frequencies while
270	 * DPLL core pin properties requires list of frequency ranges.
271	 * So read the frequency list into temporary array.
272	 */
273	freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL);
274	if (!freqs) {
275		rc = -ENOMEM;
276		goto err_alloc_freqs;
277	}
278
279	/* Read frequencies list from firmware node */
280	fwnode_property_read_u64_array(props->fwnode,
281				       "supported-frequencies-hz", freqs,
282				       num_freqs);
283
284	/* Allocate frequency ranges list and fill it */
285	ranges = kcalloc(num_freqs, sizeof(*ranges), GFP_KERNEL);
286	if (!ranges) {
287		rc = -ENOMEM;
288		goto err_alloc_ranges;
289	}
290
291	/* Convert list of frequencies to list of frequency ranges but
292	 * filter-out frequencies that are not representable by device
293	 */
294	for (i = 0, j = 0; i < num_freqs; i++) {
295		struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]);
296
297		if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) {
298			ranges[j] = freq;
299			j++;
300		}
301	}
302
303	/* Save number of freq ranges and pointer to them into pin properties */
304	props->dpll_props.freq_supported = ranges;
305	props->dpll_props.freq_supported_num = j;
306
307	/* Free temporary array */
308	kfree(freqs);
309
310	return props;
311
312err_alloc_ranges:
313	kfree(freqs);
314err_alloc_freqs:
315	fwnode_handle_put(props->fwnode);
316	kfree(props);
317
318	return ERR_PTR(rc);
319}
320
321/**
322 * zl3073x_pin_props_put - release pin properties
323 * @props: pin properties to free
324 *
325 * The function deallocates given pin properties structure.
326 */
327void zl3073x_pin_props_put(struct zl3073x_pin_props *props)
328{
329	/* Free supported frequency ranges list if it is present */
330	kfree(props->dpll_props.freq_supported);
331
332	/* Put firmware handle if it is present */
333	if (props->fwnode)
334		fwnode_handle_put(props->fwnode);
335
336	kfree(props);
337}
338
339/**
340 * zl3073x_prop_dpll_type_get - get DPLL channel type
341 * @zldev: pointer to zl3073x device
342 * @index: DPLL channel index
343 *
344 * Return: DPLL type for given DPLL channel
345 */
346enum dpll_type
347zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index)
348{
349	const char *types[ZL3073X_MAX_CHANNELS];
350	int count;
351
352	/* Read dpll types property from firmware */
353	count = device_property_read_string_array(zldev->dev, "dpll-types",
354						  types, ARRAY_SIZE(types));
355
356	/* Return default if property or entry for given channel is missing */
357	if (index >= count)
358		return DPLL_TYPE_PPS;
359
360	if (!strcmp(types[index], "pps"))
361		return DPLL_TYPE_PPS;
362	else if (!strcmp(types[index], "eec"))
363		return DPLL_TYPE_EEC;
364
365	dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n",
366		 types[index]);
367
368	return DPLL_TYPE_PPS; /* Default */
369}
~/ Read-only file preview.