Branch data Line data Source code
1 : : /* SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0)
2 : : *
3 : : * Copyright 2010-2016 Freescale Semiconductor Inc.
4 : : * Copyright 2017 NXP
5 : : *
6 : : */
7 : :
8 : : #include <dpaa_of.h>
9 : : #include <assert.h>
10 : : #include <rte_string_fns.h>
11 : : #include <dpaax_logs.h>
12 : :
13 : : static int alive;
14 : : static struct dt_dir root_dir;
15 : : static const char *base_dir;
16 : : static COMPAT_LIST_HEAD(linear);
17 : :
18 : : static int
19 : 0 : of_open_dir(const char *relative_path, struct dirent ***d)
20 : : {
21 : : int ret;
22 : : char full_path[PATH_MAX];
23 : :
24 : 0 : snprintf(full_path, PATH_MAX, "%s/%s", base_dir, relative_path);
25 : 0 : ret = scandir(full_path, d, 0, versionsort);
26 [ # # ]: 0 : if (ret < 0)
27 : 0 : DPAAX_LOG(ERR, "Failed to open directory %s",
28 : : full_path);
29 : 0 : return ret;
30 : : }
31 : :
32 : : static void
33 : 0 : of_close_dir(struct dirent **d, int num)
34 : : {
35 [ # # ]: 0 : while (num--)
36 : 0 : free(d[num]);
37 : 0 : free(d);
38 : 0 : }
39 : :
40 : : static int
41 : 0 : of_open_file(const char *relative_path)
42 : : {
43 : : int ret;
44 : : char full_path[PATH_MAX];
45 : :
46 : 0 : snprintf(full_path, PATH_MAX, "%s/%s", base_dir, relative_path);
47 : : ret = open(full_path, O_RDONLY);
48 [ # # ]: 0 : if (ret < 0)
49 : 0 : DPAAX_LOG(ERR, "Failed to open directory %s",
50 : : full_path);
51 : 0 : return ret;
52 : : }
53 : :
54 : : static void
55 : 0 : process_file(struct dirent *dent, struct dt_dir *parent)
56 : : {
57 : : int fd;
58 : 0 : struct dt_file *f = malloc(sizeof(*f));
59 : :
60 [ # # ]: 0 : if (!f) {
61 : 0 : DPAAX_LOG(DEBUG, "Unable to allocate memory for file node");
62 : 0 : return;
63 : : }
64 : 0 : f->node.is_file = 1;
65 : 0 : strlcpy(f->node.node.name, dent->d_name, NAME_MAX);
66 : 0 : snprintf(f->node.node.full_name, PATH_MAX, "%s/%s",
67 : 0 : parent->node.node.full_name, dent->d_name);
68 : 0 : f->parent = parent;
69 : 0 : fd = of_open_file(f->node.node.full_name);
70 [ # # ]: 0 : if (fd < 0) {
71 : 0 : DPAAX_LOG(DEBUG, "Unable to open file node");
72 : 0 : free(f);
73 : 0 : return;
74 : : }
75 [ # # ]: 0 : f->len = read(fd, f->buf, OF_FILE_BUF_MAX);
76 : 0 : close(fd);
77 [ # # ]: 0 : if (f->len < 0) {
78 : 0 : DPAAX_LOG(DEBUG, "Unable to read file node");
79 : 0 : free(f);
80 : 0 : return;
81 : : }
82 : 0 : list_add_tail(&f->node.list, &parent->files);
83 : : }
84 : :
85 : : static const struct dt_dir *
86 : : node2dir(const struct device_node *n)
87 : : {
88 : : struct dt_node *dn = container_of((struct device_node *)n,
89 : : struct dt_node, node);
90 : : const struct dt_dir *d = container_of(dn, struct dt_dir, node);
91 : :
92 [ # # # # : 0 : assert(!dn->is_file);
# # # # #
# # # # #
# # # # #
# ]
93 : : return d;
94 : : }
95 : :
96 : : /* process_dir() calls iterate_dir(), but the latter will also call the former
97 : : * when recursing into sub-directories, so a predeclaration is needed.
98 : : */
99 : : static int process_dir(const char *relative_path, struct dt_dir *dt);
100 : :
101 : : static int
102 : 0 : iterate_dir(struct dirent **d, int num, struct dt_dir *dt)
103 : : {
104 : : int loop;
105 : : /* Iterate the directory contents */
106 [ # # ]: 0 : for (loop = 0; loop < num; loop++) {
107 : : struct dt_dir *subdir;
108 : : int ret;
109 : : /* Ignore dot files of all types (especially "..") */
110 [ # # ]: 0 : if (d[loop]->d_name[0] == '.')
111 : 0 : continue;
112 [ # # # ]: 0 : switch (d[loop]->d_type) {
113 : 0 : case DT_REG:
114 : 0 : process_file(d[loop], dt);
115 : 0 : break;
116 : 0 : case DT_DIR:
117 : 0 : subdir = malloc(sizeof(*subdir));
118 [ # # ]: 0 : if (!subdir) {
119 : 0 : perror("malloc");
120 : 0 : return -ENOMEM;
121 : : }
122 : 0 : strlcpy(subdir->node.node.name, d[loop]->d_name,
123 : : NAME_MAX);
124 : 0 : snprintf(subdir->node.node.full_name, PATH_MAX,
125 : 0 : "%s/%s", dt->node.node.full_name,
126 : 0 : d[loop]->d_name);
127 : 0 : subdir->parent = dt;
128 : 0 : ret = process_dir(subdir->node.node.full_name, subdir);
129 [ # # ]: 0 : if (ret)
130 : 0 : return ret;
131 : 0 : list_add_tail(&subdir->node.list, &dt->subdirs);
132 : 0 : break;
133 : 0 : default:
134 : 0 : DPAAX_LOG(DEBUG, "Ignoring invalid dt entry %s/%s",
135 : : dt->node.node.full_name, d[loop]->d_name);
136 : : }
137 : : }
138 : : return 0;
139 : : }
140 : :
141 : : static int
142 : 0 : process_dir(const char *relative_path, struct dt_dir *dt)
143 : : {
144 : : struct dirent **d;
145 : : int ret, num;
146 : :
147 : 0 : dt->node.is_file = 0;
148 : 0 : INIT_LIST_HEAD(&dt->subdirs);
149 : 0 : INIT_LIST_HEAD(&dt->files);
150 : 0 : ret = of_open_dir(relative_path, &d);
151 [ # # ]: 0 : if (ret < 0)
152 : : return ret;
153 : : num = ret;
154 : 0 : ret = iterate_dir(d, num, dt);
155 : 0 : of_close_dir(d, num);
156 : 0 : return (ret < 0) ? ret : 0;
157 : : }
158 : :
159 : : static void
160 : 0 : linear_dir(struct dt_dir *d)
161 : : {
162 : : struct dt_file *f;
163 : : struct dt_dir *dd;
164 : :
165 : 0 : d->compatible = NULL;
166 : 0 : d->status = NULL;
167 : 0 : d->lphandle = NULL;
168 : 0 : d->a_cells = NULL;
169 : 0 : d->s_cells = NULL;
170 : 0 : d->reg = NULL;
171 [ # # ]: 0 : list_for_each_entry(f, &d->files, node.list) {
172 [ # # ]: 0 : if (!strcmp(f->node.node.name, "compatible")) {
173 [ # # ]: 0 : if (d->compatible)
174 : 0 : DPAAX_LOG(DEBUG, "Duplicate compatible in"
175 : : " %s", d->node.node.full_name);
176 : 0 : d->compatible = f;
177 [ # # ]: 0 : } else if (!strcmp(f->node.node.name, "status")) {
178 [ # # ]: 0 : if (d->status)
179 : 0 : DPAAX_LOG(DEBUG, "Duplicate status in %s",
180 : : d->node.node.full_name);
181 : 0 : d->status = f;
182 [ # # ]: 0 : } else if (!strcmp(f->node.node.name, "linux,phandle")) {
183 [ # # ]: 0 : if (d->lphandle)
184 : 0 : DPAAX_LOG(DEBUG, "Duplicate lphandle in %s",
185 : : d->node.node.full_name);
186 : 0 : d->lphandle = f;
187 [ # # ]: 0 : } else if (!strcmp(f->node.node.name, "phandle")) {
188 [ # # ]: 0 : if (d->lphandle)
189 : 0 : DPAAX_LOG(DEBUG, "Duplicate lphandle in %s",
190 : : d->node.node.full_name);
191 : 0 : d->lphandle = f;
192 [ # # ]: 0 : } else if (!strcmp(f->node.node.name, "#address-cells")) {
193 [ # # ]: 0 : if (d->a_cells)
194 : 0 : DPAAX_LOG(DEBUG, "Duplicate a_cells in %s",
195 : : d->node.node.full_name);
196 : 0 : d->a_cells = f;
197 [ # # ]: 0 : } else if (!strcmp(f->node.node.name, "#size-cells")) {
198 [ # # ]: 0 : if (d->s_cells)
199 : 0 : DPAAX_LOG(DEBUG, "Duplicate s_cells in %s",
200 : : d->node.node.full_name);
201 : 0 : d->s_cells = f;
202 [ # # ]: 0 : } else if (!strcmp(f->node.node.name, "reg")) {
203 [ # # ]: 0 : if (d->reg)
204 : 0 : DPAAX_LOG(DEBUG, "Duplicate reg in %s",
205 : : d->node.node.full_name);
206 : 0 : d->reg = f;
207 : : }
208 : : }
209 : :
210 [ # # ]: 0 : list_for_each_entry(dd, &d->subdirs, node.list) {
211 : 0 : list_add_tail(&dd->linear, &linear);
212 : 0 : linear_dir(dd);
213 : : }
214 : 0 : }
215 : :
216 : : int
217 : 0 : of_init_path(const char *dt_path)
218 : : {
219 : : int ret;
220 : :
221 : 0 : base_dir = dt_path;
222 : :
223 : : /* This needs to be singleton initialization */
224 : : DPAAX_HWWARN(alive, "Double-init of device-tree driver!");
225 : :
226 : : /* Prepare root node (the remaining fields are set in process_dir()) */
227 : 0 : root_dir.node.node.name[0] = '\0';
228 : 0 : root_dir.node.node.full_name[0] = '\0';
229 : 0 : INIT_LIST_HEAD(&root_dir.node.list);
230 : 0 : root_dir.parent = NULL;
231 : :
232 : : /* Kick things off... */
233 : 0 : ret = process_dir("", &root_dir);
234 [ # # ]: 0 : if (ret) {
235 : 0 : DPAAX_LOG(ERR, "Unable to parse device tree");
236 : 0 : return ret;
237 : : }
238 : :
239 : : /* Now make a flat, linear list of directories */
240 : 0 : linear_dir(&root_dir);
241 : 0 : alive = 1;
242 : 0 : return 0;
243 : : }
244 : :
245 : : static void
246 : 0 : destroy_dir(struct dt_dir *d)
247 : : {
248 : : struct dt_file *f, *tmpf;
249 : : struct dt_dir *dd, *tmpd;
250 : :
251 [ # # ]: 0 : list_for_each_entry_safe(f, tmpf, &d->files, node.list) {
252 : 0 : list_del(&f->node.list);
253 : 0 : free(f);
254 : : }
255 [ # # ]: 0 : list_for_each_entry_safe(dd, tmpd, &d->subdirs, node.list) {
256 : 0 : destroy_dir(dd);
257 : 0 : list_del(&dd->node.list);
258 : 0 : free(dd);
259 : : }
260 : 0 : }
261 : :
262 : : void
263 : 0 : of_finish(void)
264 : : {
265 : : DPAAX_HWWARN(!alive, "Double-finish of device-tree driver!");
266 : :
267 : 0 : destroy_dir(&root_dir);
268 : 0 : INIT_LIST_HEAD(&linear);
269 : 0 : alive = 0;
270 : 0 : }
271 : :
272 : : static const struct dt_dir *
273 : : next_linear(const struct dt_dir *f)
274 : : {
275 [ # # # # ]: 0 : if (f->linear.next == &linear)
276 : : return NULL;
277 : 0 : return list_entry(f->linear.next, struct dt_dir, linear);
278 : : }
279 : :
280 : : static int
281 : 0 : check_compatible(const struct dt_file *f, const char *compatible)
282 : : {
283 : 0 : const char *c = (char *)f->buf;
284 : 0 : unsigned int len, remains = f->len;
285 : :
286 [ # # ]: 0 : while (remains) {
287 : 0 : len = strlen(c);
288 [ # # ]: 0 : if (!strcmp(c, compatible))
289 : : return 1;
290 : :
291 [ # # ]: 0 : if (remains < len + 1)
292 : : break;
293 : :
294 : 0 : c += (len + 1);
295 : 0 : remains -= (len + 1);
296 : : }
297 : : return 0;
298 : : }
299 : :
300 : : const struct device_node *
301 : 0 : of_find_compatible_node(const struct device_node *from,
302 : : const char *type __rte_unused,
303 : : const char *compatible)
304 : : {
305 : : const struct dt_dir *d;
306 : :
307 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
308 : :
309 [ # # # # ]: 0 : if (list_empty(&linear))
310 : : return NULL;
311 [ # # ]: 0 : if (!from)
312 : 0 : d = list_entry(linear.next, struct dt_dir, linear);
313 : : else
314 : : d = node2dir(from);
315 [ # # # # : 0 : for (d = next_linear(d); d && (!d->compatible ||
# # ]
316 : 0 : !check_compatible(d->compatible,
317 : : compatible));
318 : : d = next_linear(d))
319 : : ;
320 [ # # ]: 0 : if (d)
321 : 0 : return &d->node.node;
322 : : return NULL;
323 : : }
324 : :
325 : : const void *
326 [ # # ]: 0 : of_get_property(const struct device_node *from, const char *name,
327 : : size_t *lenp)
328 : : {
329 : : const struct dt_dir *d;
330 : : const struct dt_file *f;
331 : :
332 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
333 : :
334 : : d = node2dir(from);
335 [ # # ]: 0 : list_for_each_entry(f, &d->files, node.list)
336 [ # # ]: 0 : if (!strcmp(f->node.node.name, name)) {
337 [ # # ]: 0 : if (lenp)
338 : 0 : *lenp = f->len;
339 : 0 : return f->buf;
340 : : }
341 : : return NULL;
342 : : }
343 : :
344 : : bool
345 [ # # ]: 0 : of_device_is_available(const struct device_node *dev_node)
346 : : {
347 : : const struct dt_dir *d;
348 : :
349 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
350 : : d = node2dir(dev_node);
351 [ # # ]: 0 : if (!d->status)
352 : : return true;
353 [ # # ]: 0 : if (!strcmp((char *)d->status->buf, "okay"))
354 : : return true;
355 [ # # ]: 0 : if (!strcmp((char *)d->status->buf, "ok"))
356 : 0 : return true;
357 : : return false;
358 : : }
359 : :
360 : : const struct device_node *
361 : 0 : of_find_node_by_phandle(uint64_t ph)
362 : : {
363 : : const struct dt_dir *d;
364 : :
365 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
366 [ # # ]: 0 : list_for_each_entry(d, &linear, linear)
367 [ # # # # ]: 0 : if (d->lphandle && (d->lphandle->len == 4) &&
368 [ # # ]: 0 : !memcmp(d->lphandle->buf, &ph, 4))
369 : 0 : return &d->node.node;
370 : : return NULL;
371 : : }
372 : :
373 : : const struct device_node *
374 : 0 : of_get_parent(const struct device_node *dev_node)
375 : : {
376 : : const struct dt_dir *d;
377 : :
378 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
379 : :
380 [ # # ]: 0 : if (!dev_node)
381 : : return NULL;
382 : : d = node2dir(dev_node);
383 [ # # ]: 0 : if (!d->parent)
384 : : return NULL;
385 : 0 : return &d->parent->node.node;
386 : : }
387 : :
388 : : const struct device_node *
389 : 0 : of_get_next_child(const struct device_node *dev_node,
390 : : const struct device_node *prev)
391 : : {
392 : : const struct dt_dir *p, *c;
393 : :
394 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
395 : :
396 [ # # ]: 0 : if (!dev_node)
397 : : return NULL;
398 : : p = node2dir(dev_node);
399 [ # # ]: 0 : if (prev) {
400 : : c = node2dir(prev);
401 : : DPAAX_HWWARN((c->parent != p), "Parent/child mismatch");
402 [ # # ]: 0 : if (c->parent != p)
403 : : return NULL;
404 [ # # ]: 0 : if (c->node.list.next == &p->subdirs)
405 : : /* prev was the last child */
406 : : return NULL;
407 : : c = list_entry(c->node.list.next, struct dt_dir, node.list);
408 : 0 : return &c->node.node;
409 : : }
410 : : /* Return first child */
411 [ # # # # ]: 0 : if (list_empty(&p->subdirs))
412 : : return NULL;
413 : : c = list_entry(p->subdirs.next, struct dt_dir, node.list);
414 : 0 : return &c->node.node;
415 : : }
416 : :
417 : : uint32_t
418 : 0 : of_n_addr_cells(const struct device_node *dev_node)
419 : : {
420 : : const struct dt_dir *d;
421 : :
422 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised");
423 [ # # ]: 0 : if (!dev_node)
424 : : return OF_DEFAULT_NA;
425 : : d = node2dir(dev_node);
426 [ # # ]: 0 : while ((d = d->parent))
427 [ # # ]: 0 : if (d->a_cells) {
428 : : unsigned char *buf =
429 : : (unsigned char *)&d->a_cells->buf[0];
430 [ # # ]: 0 : assert(d->a_cells->len == 4);
431 : 0 : return ((uint32_t)buf[0] << 24) |
432 : 0 : ((uint32_t)buf[1] << 16) |
433 : 0 : ((uint32_t)buf[2] << 8) |
434 : 0 : (uint32_t)buf[3];
435 : : }
436 : : return OF_DEFAULT_NA;
437 : : }
438 : :
439 : : uint32_t
440 : 0 : of_n_size_cells(const struct device_node *dev_node)
441 : : {
442 : : const struct dt_dir *d;
443 : :
444 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
445 [ # # ]: 0 : if (!dev_node)
446 : : return OF_DEFAULT_NA;
447 : : d = node2dir(dev_node);
448 [ # # ]: 0 : while ((d = d->parent))
449 [ # # ]: 0 : if (d->s_cells) {
450 : : unsigned char *buf =
451 : : (unsigned char *)&d->s_cells->buf[0];
452 [ # # ]: 0 : assert(d->s_cells->len == 4);
453 : 0 : return ((uint32_t)buf[0] << 24) |
454 : 0 : ((uint32_t)buf[1] << 16) |
455 : 0 : ((uint32_t)buf[2] << 8) |
456 : 0 : (uint32_t)buf[3];
457 : : }
458 : : return OF_DEFAULT_NS;
459 : : }
460 : :
461 : : const uint32_t *
462 : 0 : of_get_address(const struct device_node *dev_node, size_t idx,
463 : : uint64_t *size, uint32_t *flags __rte_unused)
464 : : {
465 : : const struct dt_dir *d;
466 : : const unsigned char *buf;
467 : 0 : uint32_t na = of_n_addr_cells(dev_node);
468 : 0 : uint32_t ns = of_n_size_cells(dev_node);
469 : :
470 [ # # ]: 0 : if (!dev_node)
471 : : d = &root_dir;
472 : : else
473 : : d = node2dir(dev_node);
474 [ # # ]: 0 : if (!d->reg)
475 : : return NULL;
476 [ # # ]: 0 : assert(d->reg->len % ((na + ns) * 4) == 0);
477 [ # # ]: 0 : assert(d->reg->len / ((na + ns) * 4) > (unsigned int) idx);
478 : 0 : buf = (const unsigned char *)&d->reg->buf[0];
479 : 0 : buf += (na + ns) * idx * 4;
480 [ # # ]: 0 : if (size)
481 [ # # ]: 0 : for (*size = 0; ns > 0; ns--, na++)
482 : 0 : *size = (*size << 32) +
483 : 0 : (((uint32_t)buf[4 * na] << 24) |
484 : 0 : ((uint32_t)buf[4 * na + 1] << 16) |
485 : 0 : ((uint32_t)buf[4 * na + 2] << 8) |
486 : 0 : (uint32_t)buf[4 * na + 3]);
487 : : return (const uint32_t *)buf;
488 : : }
489 : :
490 : : uint64_t
491 : 0 : of_translate_address(const struct device_node *dev_node,
492 : : const uint32_t *addr)
493 : : {
494 : : uint64_t phys_addr, tmp_addr;
495 : : const struct device_node *parent;
496 : : const uint32_t *ranges;
497 : : size_t rlen;
498 : : uint32_t na, pna;
499 : :
500 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
501 : : assert(dev_node != NULL);
502 : :
503 : 0 : na = of_n_addr_cells(dev_node);
504 : 0 : phys_addr = of_read_number(addr, na);
505 : :
506 : 0 : dev_node = of_get_parent(dev_node);
507 [ # # ]: 0 : if (!dev_node)
508 : : return 0;
509 [ # # ]: 0 : else if (node2dir(dev_node) == &root_dir)
510 : : return phys_addr;
511 : :
512 : : do {
513 : 0 : pna = of_n_addr_cells(dev_node);
514 : 0 : parent = of_get_parent(dev_node);
515 [ # # ]: 0 : if (!parent)
516 : : return 0;
517 : :
518 : 0 : ranges = of_get_property(dev_node, "ranges", &rlen);
519 : : /* "ranges" property is missing. Translation breaks */
520 [ # # ]: 0 : if (!ranges)
521 : : return 0;
522 : : /* "ranges" property is empty. Do 1:1 translation */
523 [ # # ]: 0 : else if (rlen == 0)
524 : 0 : continue;
525 : : else
526 : 0 : tmp_addr = of_read_number(ranges + na, pna);
527 : :
528 : : na = pna;
529 : : dev_node = parent;
530 : 0 : phys_addr += tmp_addr;
531 [ # # ]: 0 : } while (node2dir(parent) != &root_dir);
532 : :
533 : : return phys_addr;
534 : : }
535 : :
536 : : bool
537 : 0 : of_device_is_compatible(const struct device_node *dev_node,
538 : : const char *compatible)
539 : : {
540 : : const struct dt_dir *d;
541 : :
542 : : DPAAX_HWWARN(!alive, "Device-tree driver not initialised!");
543 [ # # ]: 0 : if (!dev_node)
544 : : d = &root_dir;
545 : : else
546 : : d = node2dir(dev_node);
547 [ # # # # ]: 0 : if (d->compatible && check_compatible(d->compatible, compatible))
548 : 0 : return true;
549 : : return false;
550 : : }
551 : :
552 : : static const void *of_get_mac_addr(const struct device_node *np,
553 : : const char *name)
554 : : {
555 : 0 : return of_get_property(np, name, NULL);
556 : : }
557 : :
558 : : /**
559 : : * Search the device tree for the best MAC address to use. 'mac-address' is
560 : : * checked first, because that is supposed to contain to "most recent" MAC
561 : : * address. If that isn't set, then 'local-mac-address' is checked next,
562 : : * because that is the default address. If that isn't set, then the obsolete
563 : : * 'address' is checked, just in case we're using an old device tree.
564 : : *
565 : : * Note that the 'address' property is supposed to contain a virtual address of
566 : : * the register set, but some DTS files have redefined that property to be the
567 : : * MAC address.
568 : : *
569 : : * All-zero MAC addresses are rejected, because those could be properties that
570 : : * exist in the device tree, but were not set by U-Boot. For example, the
571 : : * DTS could define 'mac-address' and 'local-mac-address', with zero MAC
572 : : * addresses. Some older U-Boots only initialized 'local-mac-address'. In
573 : : * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists
574 : : * but is all zeros.
575 : : */
576 : 0 : const void *of_get_mac_address(const struct device_node *np)
577 : : {
578 : : const void *addr;
579 : :
580 : : addr = of_get_mac_addr(np, "mac-address");
581 [ # # ]: 0 : if (addr)
582 : : return addr;
583 : :
584 : : addr = of_get_mac_addr(np, "local-mac-address");
585 [ # # ]: 0 : if (addr)
586 : : return addr;
587 : :
588 : 0 : return of_get_mac_addr(np, "address");
589 : : }
|