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", ®))
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