xref: /linux/drivers/dpll/zl3073x/prop.c (revision bf4afc53b77aeaa48b5409da5c8da6bb4eff7f43)
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
zl3073x_pin_check_freq(struct zl3073x_dev * zldev,enum dpll_pin_direction dir,u8 id,u64 freq)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
zl3073x_prop_pin_package_label_set(struct zl3073x_dev * zldev,struct zl3073x_pin_props * props,enum dpll_pin_direction dir,u8 id)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
zl3073x_prop_pin_fwnode_get(struct zl3073x_dev * zldev,struct zl3073x_pin_props * props,enum dpll_pin_direction dir,u8 id)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  */
zl3073x_pin_props_get(struct zl3073x_dev * zldev,enum dpll_pin_direction dir,u8 index)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 = 0, rc;
197 	u64 *freqs = NULL;
198 	const char *type;
199 	u32 curr_freq;
200 
201 	props = kzalloc_obj(*props);
202 	if (!props)
203 		return ERR_PTR(-ENOMEM);
204 
205 	/* Set default pin type and capabilities */
206 	if (dir == DPLL_PIN_DIRECTION_INPUT) {
207 		props->dpll_props.type = DPLL_PIN_TYPE_EXT;
208 		props->dpll_props.capabilities =
209 			DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
210 			DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
211 		curr_freq = zl3073x_dev_ref_freq_get(zldev, index);
212 	} else {
213 		u8 out, synth;
214 		u32 f;
215 
216 		props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
217 
218 		/* The output pin phase adjustment granularity equals half of
219 		 * the synth frequency count.
220 		 */
221 		out = zl3073x_output_pin_out_get(index);
222 		synth = zl3073x_dev_out_synth_get(zldev, out);
223 		f = 2 * zl3073x_dev_synth_freq_get(zldev, synth);
224 		props->dpll_props.phase_gran = f ? div_u64(PSEC_PER_SEC, f) : 1;
225 		curr_freq = zl3073x_dev_output_pin_freq_get(zldev, index);
226 	}
227 
228 	props->dpll_props.phase_range.min = S32_MIN;
229 	props->dpll_props.phase_range.max = S32_MAX;
230 
231 	zl3073x_prop_pin_package_label_set(zldev, props, dir, index);
232 
233 	/* Get firmware node for the given pin */
234 	rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index);
235 	if (rc)
236 		goto skip_fwnode_props;
237 
238 	/* Look for label property and store the value as board label */
239 	fwnode_property_read_string(props->fwnode, "label",
240 				    &props->dpll_props.board_label);
241 
242 	/* Look for pin type property and translate its value to DPLL
243 	 * pin type enum if it is present.
244 	 */
245 	if (!fwnode_property_read_string(props->fwnode, "connection-type",
246 					 &type)) {
247 		if (!strcmp(type, "ext"))
248 			props->dpll_props.type = DPLL_PIN_TYPE_EXT;
249 		else if (!strcmp(type, "gnss"))
250 			props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
251 		else if (!strcmp(type, "int"))
252 			props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
253 		else if (!strcmp(type, "synce"))
254 			props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
255 		else if (!strcmp(type, "mux"))
256 			props->dpll_props.type = DPLL_PIN_TYPE_MUX;
257 		else
258 			dev_warn(zldev->dev,
259 				 "Unknown or unsupported pin type '%s'\n",
260 				 type);
261 	}
262 
263 	/* Check if the pin supports embedded sync control */
264 	props->esync_control = fwnode_property_read_bool(props->fwnode,
265 							 "esync-control");
266 
267 	/* Read supported frequencies property if it is specified */
268 	num_freqs = fwnode_property_count_u64(props->fwnode,
269 					      "supported-frequencies-hz");
270 	if (num_freqs <= 0) {
271 		num_freqs = 0;
272 		goto skip_fwnode_props;
273 	}
274 
275 	/* The firmware node specifies list of supported frequencies while
276 	 * DPLL core pin properties requires list of frequency ranges.
277 	 * So read the frequency list into temporary array.
278 	 */
279 	freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL);
280 	if (!freqs) {
281 		rc = -ENOMEM;
282 		goto err_alloc_freqs;
283 	}
284 
285 	/* Read frequencies list from firmware node */
286 	fwnode_property_read_u64_array(props->fwnode,
287 				       "supported-frequencies-hz", freqs,
288 				       num_freqs);
289 
290 skip_fwnode_props:
291 	/* Allocate frequency ranges list - extra slot for current frequency */
292 	ranges = kzalloc_objs(*ranges, num_freqs + 1);
293 	if (!ranges) {
294 		rc = -ENOMEM;
295 		goto err_alloc_ranges;
296 	}
297 
298 	/* Start with current frequency at index 0 */
299 	ranges[0] = (struct dpll_pin_frequency)DPLL_PIN_FREQUENCY(curr_freq);
300 
301 	/* Add frequencies from firmware node, skipping current frequency
302 	 * and filtering out frequencies not representable by device
303 	 */
304 	for (i = 0, j = 1; i < num_freqs; i++) {
305 		struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]);
306 
307 		if (freqs[i] == curr_freq)
308 			continue;
309 		if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) {
310 			ranges[j] = freq;
311 			j++;
312 		}
313 	}
314 
315 	/* Save number of freq ranges and pointer to them into pin properties */
316 	props->dpll_props.freq_supported = ranges;
317 	props->dpll_props.freq_supported_num = j;
318 
319 	/* Free temporary array */
320 	kfree(freqs);
321 
322 	return props;
323 
324 err_alloc_ranges:
325 	kfree(freqs);
326 err_alloc_freqs:
327 	fwnode_handle_put(props->fwnode);
328 	kfree(props);
329 
330 	return ERR_PTR(rc);
331 }
332 
333 /**
334  * zl3073x_pin_props_put - release pin properties
335  * @props: pin properties to free
336  *
337  * The function deallocates given pin properties structure.
338  */
zl3073x_pin_props_put(struct zl3073x_pin_props * props)339 void zl3073x_pin_props_put(struct zl3073x_pin_props *props)
340 {
341 	/* Free supported frequency ranges list if it is present */
342 	kfree(props->dpll_props.freq_supported);
343 
344 	/* Put firmware handle if it is present */
345 	if (props->fwnode)
346 		fwnode_handle_put(props->fwnode);
347 
348 	kfree(props);
349 }
350 
351 /**
352  * zl3073x_prop_dpll_type_get - get DPLL channel type
353  * @zldev: pointer to zl3073x device
354  * @index: DPLL channel index
355  *
356  * Return: DPLL type for given DPLL channel
357  */
358 enum dpll_type
zl3073x_prop_dpll_type_get(struct zl3073x_dev * zldev,u8 index)359 zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index)
360 {
361 	const char *types[ZL3073X_MAX_CHANNELS];
362 	int count;
363 
364 	/* Read dpll types property from firmware */
365 	count = device_property_read_string_array(zldev->dev, "dpll-types",
366 						  types, ARRAY_SIZE(types));
367 
368 	/* Return default if property or entry for given channel is missing */
369 	if (index >= count)
370 		return DPLL_TYPE_PPS;
371 
372 	if (!strcmp(types[index], "pps"))
373 		return DPLL_TYPE_PPS;
374 	else if (!strcmp(types[index], "eec"))
375 		return DPLL_TYPE_EEC;
376 
377 	dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n",
378 		 types[index]);
379 
380 	return DPLL_TYPE_PPS; /* Default */
381 }
382