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_out_synth_get(zldev, out);
50
51 /* Get synth frequency */
52 synth_freq = zl3073x_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_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_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, 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 props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
212 }
213
214 props->dpll_props.phase_range.min = S32_MIN;
215 props->dpll_props.phase_range.max = S32_MAX;
216
217 zl3073x_prop_pin_package_label_set(zldev, props, dir, index);
218
219 /* Get firmware node for the given pin */
220 rc = zl3073x_prop_pin_fwnode_get(zldev, props, dir, index);
221 if (rc)
222 return props; /* Return if it does not exist */
223
224 /* Look for label property and store the value as board label */
225 fwnode_property_read_string(props->fwnode, "label",
226 &props->dpll_props.board_label);
227
228 /* Look for pin type property and translate its value to DPLL
229 * pin type enum if it is present.
230 */
231 if (!fwnode_property_read_string(props->fwnode, "connection-type",
232 &type)) {
233 if (!strcmp(type, "ext"))
234 props->dpll_props.type = DPLL_PIN_TYPE_EXT;
235 else if (!strcmp(type, "gnss"))
236 props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
237 else if (!strcmp(type, "int"))
238 props->dpll_props.type = DPLL_PIN_TYPE_INT_OSCILLATOR;
239 else if (!strcmp(type, "synce"))
240 props->dpll_props.type = DPLL_PIN_TYPE_SYNCE_ETH_PORT;
241 else
242 dev_warn(zldev->dev,
243 "Unknown or unsupported pin type '%s'\n",
244 type);
245 }
246
247 /* Check if the pin supports embedded sync control */
248 props->esync_control = fwnode_property_read_bool(props->fwnode,
249 "esync-control");
250
251 /* Read supported frequencies property if it is specified */
252 num_freqs = fwnode_property_count_u64(props->fwnode,
253 "supported-frequencies-hz");
254 if (num_freqs <= 0)
255 /* Return if the property does not exist or number is 0 */
256 return props;
257
258 /* The firmware node specifies list of supported frequencies while
259 * DPLL core pin properties requires list of frequency ranges.
260 * So read the frequency list into temporary array.
261 */
262 freqs = kcalloc(num_freqs, sizeof(*freqs), GFP_KERNEL);
263 if (!freqs) {
264 rc = -ENOMEM;
265 goto err_alloc_freqs;
266 }
267
268 /* Read frequencies list from firmware node */
269 fwnode_property_read_u64_array(props->fwnode,
270 "supported-frequencies-hz", freqs,
271 num_freqs);
272
273 /* Allocate frequency ranges list and fill it */
274 ranges = kcalloc(num_freqs, sizeof(*ranges), GFP_KERNEL);
275 if (!ranges) {
276 rc = -ENOMEM;
277 goto err_alloc_ranges;
278 }
279
280 /* Convert list of frequencies to list of frequency ranges but
281 * filter-out frequencies that are not representable by device
282 */
283 for (i = 0, j = 0; i < num_freqs; i++) {
284 struct dpll_pin_frequency freq = DPLL_PIN_FREQUENCY(freqs[i]);
285
286 if (zl3073x_pin_check_freq(zldev, dir, index, freqs[i])) {
287 ranges[j] = freq;
288 j++;
289 }
290 }
291
292 /* Save number of freq ranges and pointer to them into pin properties */
293 props->dpll_props.freq_supported = ranges;
294 props->dpll_props.freq_supported_num = j;
295
296 /* Free temporary array */
297 kfree(freqs);
298
299 return props;
300
301 err_alloc_ranges:
302 kfree(freqs);
303 err_alloc_freqs:
304 fwnode_handle_put(props->fwnode);
305 kfree(props);
306
307 return ERR_PTR(rc);
308 }
309
310 /**
311 * zl3073x_pin_props_put - release pin properties
312 * @props: pin properties to free
313 *
314 * The function deallocates given pin properties structure.
315 */
zl3073x_pin_props_put(struct zl3073x_pin_props * props)316 void zl3073x_pin_props_put(struct zl3073x_pin_props *props)
317 {
318 /* Free supported frequency ranges list if it is present */
319 kfree(props->dpll_props.freq_supported);
320
321 /* Put firmware handle if it is present */
322 if (props->fwnode)
323 fwnode_handle_put(props->fwnode);
324
325 kfree(props);
326 }
327
328 /**
329 * zl3073x_prop_dpll_type_get - get DPLL channel type
330 * @zldev: pointer to zl3073x device
331 * @index: DPLL channel index
332 *
333 * Return: DPLL type for given DPLL channel
334 */
335 enum dpll_type
zl3073x_prop_dpll_type_get(struct zl3073x_dev * zldev,u8 index)336 zl3073x_prop_dpll_type_get(struct zl3073x_dev *zldev, u8 index)
337 {
338 const char *types[ZL3073X_MAX_CHANNELS];
339 int count;
340
341 /* Read dpll types property from firmware */
342 count = device_property_read_string_array(zldev->dev, "dpll-types",
343 types, ARRAY_SIZE(types));
344
345 /* Return default if property or entry for given channel is missing */
346 if (index >= count)
347 return DPLL_TYPE_PPS;
348
349 if (!strcmp(types[index], "pps"))
350 return DPLL_TYPE_PPS;
351 else if (!strcmp(types[index], "eec"))
352 return DPLL_TYPE_EEC;
353
354 dev_info(zldev->dev, "Unknown DPLL type '%s', using default\n",
355 types[index]);
356
357 return DPLL_TYPE_PPS; /* Default */
358 }
359