Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/dpll/zl3073x/prop.c
50622 views
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
371