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