xref: /linux/drivers/dpll/zl3073x/prop.c (revision 8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88)
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  */
29 static bool
30 zl3073x_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 
61 err_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  */
83 static void
84 zl3073x_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  */
128 static int
129 zl3073x_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  */
190 struct 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 
312 err_alloc_ranges:
313 	kfree(freqs);
314 err_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  */
327 void 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  */
346 enum dpll_type
347 zl3073x_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 }
370