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