Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/clk/clk.c
39534 views
1
/*-
2
* Copyright 2016 Michal Meloun <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/cdefs.h>
28
#include "opt_platform.h"
29
#include <sys/param.h>
30
#include <sys/conf.h>
31
#include <sys/bus.h>
32
#include <sys/kernel.h>
33
#include <sys/queue.h>
34
#include <sys/kobj.h>
35
#include <sys/malloc.h>
36
#include <sys/mutex.h>
37
#include <sys/limits.h>
38
#include <sys/lock.h>
39
#include <sys/sbuf.h>
40
#include <sys/sysctl.h>
41
#include <sys/systm.h>
42
#include <sys/sx.h>
43
44
#ifdef FDT
45
#include <dev/fdt/fdt_common.h>
46
#include <dev/ofw/ofw_bus.h>
47
#include <dev/ofw/ofw_bus_subr.h>
48
#endif
49
#include <dev/clk/clk.h>
50
51
SYSCTL_NODE(_hw, OID_AUTO, clock, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
52
"Clocks");
53
54
MALLOC_DEFINE(M_CLOCK, "clocks", "Clock framework");
55
56
/* Forward declarations. */
57
struct clk;
58
struct clknodenode;
59
struct clkdom;
60
61
typedef TAILQ_HEAD(clknode_list, clknode) clknode_list_t;
62
typedef TAILQ_HEAD(clkdom_list, clkdom) clkdom_list_t;
63
64
/* Default clock methods. */
65
static int clknode_method_init(struct clknode *clk, device_t dev);
66
static int clknode_method_recalc_freq(struct clknode *clk, uint64_t *freq);
67
static int clknode_method_set_freq(struct clknode *clk, uint64_t fin,
68
uint64_t *fout, int flags, int *stop);
69
static int clknode_method_set_gate(struct clknode *clk, bool enable);
70
static int clknode_method_set_mux(struct clknode *clk, int idx);
71
72
/*
73
* Clock controller methods.
74
*/
75
static clknode_method_t clknode_methods[] = {
76
CLKNODEMETHOD(clknode_init, clknode_method_init),
77
CLKNODEMETHOD(clknode_recalc_freq, clknode_method_recalc_freq),
78
CLKNODEMETHOD(clknode_set_freq, clknode_method_set_freq),
79
CLKNODEMETHOD(clknode_set_gate, clknode_method_set_gate),
80
CLKNODEMETHOD(clknode_set_mux, clknode_method_set_mux),
81
82
CLKNODEMETHOD_END
83
};
84
DEFINE_CLASS_0(clknode, clknode_class, clknode_methods, 0);
85
86
/*
87
* Clock node - basic element for modeling SOC clock graph. It holds the clock
88
* provider's data about the clock, and the links for the clock's membership in
89
* various lists.
90
*/
91
struct clknode {
92
KOBJ_FIELDS;
93
94
/* Clock nodes topology. */
95
struct clkdom *clkdom; /* Owning clock domain */
96
TAILQ_ENTRY(clknode) clkdom_link; /* Domain list entry */
97
TAILQ_ENTRY(clknode) clklist_link; /* Global list entry */
98
99
/* String based parent list. */
100
const char **parent_names; /* Array of parent names */
101
int parent_cnt; /* Number of parents */
102
int parent_idx; /* Parent index or -1 */
103
104
/* Cache for already resolved names. */
105
struct clknode **parents; /* Array of potential parents */
106
struct clknode *parent; /* Current parent */
107
108
/* Parent/child relationship links. */
109
clknode_list_t children; /* List of our children */
110
TAILQ_ENTRY(clknode) sibling_link; /* Our entry in parent's list */
111
112
/* Details of this device. */
113
void *softc; /* Instance softc */
114
const char *name; /* Globally unique name */
115
intptr_t id; /* Per domain unique id */
116
int flags; /* CLK_FLAG_* */
117
struct sx lock; /* Lock for this clock */
118
int ref_cnt; /* Reference counter */
119
int enable_cnt; /* Enabled counter */
120
121
/* Cached values. */
122
uint64_t freq; /* Actual frequency */
123
124
struct sysctl_ctx_list sysctl_ctx;
125
};
126
127
/*
128
* Per consumer data, information about how a consumer is using a clock node.
129
* A pointer to this structure is used as a handle in the consumer interface.
130
*/
131
struct clk {
132
device_t dev;
133
struct clknode *clknode;
134
int enable_cnt;
135
};
136
137
/*
138
* Clock domain - a group of clocks provided by one clock device.
139
*/
140
struct clkdom {
141
device_t dev; /* Link to provider device */
142
TAILQ_ENTRY(clkdom) link; /* Global domain list entry */
143
clknode_list_t clknode_list; /* All clocks in the domain */
144
145
#ifdef FDT
146
clknode_ofw_mapper_func *ofw_mapper; /* Find clock using FDT xref */
147
#endif
148
};
149
150
/*
151
* The system-wide list of clock domains.
152
*/
153
static clkdom_list_t clkdom_list = TAILQ_HEAD_INITIALIZER(clkdom_list);
154
155
/*
156
* Each clock node is linked on a system-wide list and can be searched by name.
157
*/
158
static clknode_list_t clknode_list = TAILQ_HEAD_INITIALIZER(clknode_list);
159
160
/*
161
* Locking - we use three levels of locking:
162
* - First, topology lock is taken. This one protect all lists.
163
* - Second level is per clknode lock. It protects clknode data.
164
* - Third level is outside of this file, it protect clock device registers.
165
* First two levels use sleepable locks; clock device can use mutex or sx lock.
166
*/
167
static struct sx clk_topo_lock;
168
SX_SYSINIT(clock_topology, &clk_topo_lock, "Clock topology lock");
169
170
#define CLK_TOPO_SLOCK() sx_slock(&clk_topo_lock)
171
#define CLK_TOPO_XLOCK() sx_xlock(&clk_topo_lock)
172
#define CLK_TOPO_UNLOCK() sx_unlock(&clk_topo_lock)
173
#define CLK_TOPO_ASSERT() sx_assert(&clk_topo_lock, SA_LOCKED)
174
#define CLK_TOPO_XASSERT() sx_assert(&clk_topo_lock, SA_XLOCKED)
175
176
#define CLKNODE_SLOCK(_sc) sx_slock(&((_sc)->lock))
177
#define CLKNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock))
178
#define CLKNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock))
179
180
static void clknode_adjust_parent(struct clknode *clknode, int idx);
181
182
enum clknode_sysctl_type {
183
CLKNODE_SYSCTL_PARENT,
184
CLKNODE_SYSCTL_PARENTS_LIST,
185
CLKNODE_SYSCTL_CHILDREN_LIST,
186
CLKNODE_SYSCTL_FREQUENCY,
187
CLKNODE_SYSCTL_GATE,
188
};
189
190
static int clknode_sysctl(SYSCTL_HANDLER_ARGS);
191
static int clkdom_sysctl(SYSCTL_HANDLER_ARGS);
192
193
static void clknode_finish(void *dummy);
194
SYSINIT(clknode_finish, SI_SUB_LAST, SI_ORDER_ANY, clknode_finish, NULL);
195
196
/*
197
* Default clock methods for base class.
198
*/
199
static int
200
clknode_method_init(struct clknode *clknode, device_t dev)
201
{
202
203
return (0);
204
}
205
206
static int
207
clknode_method_recalc_freq(struct clknode *clknode, uint64_t *freq)
208
{
209
210
return (0);
211
}
212
213
static int
214
clknode_method_set_freq(struct clknode *clknode, uint64_t fin, uint64_t *fout,
215
int flags, int *stop)
216
{
217
218
*stop = 0;
219
return (0);
220
}
221
222
static int
223
clknode_method_set_gate(struct clknode *clk, bool enable)
224
{
225
226
return (0);
227
}
228
229
static int
230
clknode_method_set_mux(struct clknode *clk, int idx)
231
{
232
233
return (0);
234
}
235
236
/*
237
* Internal functions.
238
*/
239
240
/*
241
* Duplicate an array of parent names.
242
*
243
* Compute total size and allocate a single block which holds both the array of
244
* pointers to strings and the copied strings themselves. Returns a pointer to
245
* the start of the block where the array of copied string pointers lives.
246
*
247
* XXX Revisit this, no need for the DECONST stuff.
248
*/
249
static const char **
250
strdup_list(const char **names, int num)
251
{
252
size_t len, slen;
253
const char **outptr, *ptr;
254
int i;
255
256
len = sizeof(char *) * num;
257
for (i = 0; i < num; i++) {
258
if (names[i] == NULL)
259
continue;
260
slen = strlen(names[i]);
261
if (slen == 0)
262
panic("Clock parent names array have empty string");
263
len += slen + 1;
264
}
265
outptr = malloc(len, M_CLOCK, M_WAITOK | M_ZERO);
266
ptr = (char *)(outptr + num);
267
for (i = 0; i < num; i++) {
268
if (names[i] == NULL)
269
continue;
270
outptr[i] = ptr;
271
slen = strlen(names[i]) + 1;
272
bcopy(names[i], __DECONST(void *, outptr[i]), slen);
273
ptr += slen;
274
}
275
return (outptr);
276
}
277
278
/*
279
* Recompute the cached frequency for this node and all its children.
280
*/
281
static int
282
clknode_refresh_cache(struct clknode *clknode, uint64_t freq)
283
{
284
int rv;
285
struct clknode *entry;
286
287
CLK_TOPO_XASSERT();
288
289
/* Compute generated frequency. */
290
rv = CLKNODE_RECALC_FREQ(clknode, &freq);
291
if (rv != 0) {
292
/* XXX If an error happens while refreshing children
293
* this leaves the world in a partially-updated state.
294
* Panic for now.
295
*/
296
panic("clknode_refresh_cache failed for '%s'\n",
297
clknode->name);
298
return (rv);
299
}
300
/* Refresh cache for this node. */
301
clknode->freq = freq;
302
303
/* Refresh cache for all children. */
304
TAILQ_FOREACH(entry, &(clknode->children), sibling_link) {
305
rv = clknode_refresh_cache(entry, freq);
306
if (rv != 0)
307
return (rv);
308
}
309
return (0);
310
}
311
312
/*
313
* Public interface.
314
*/
315
316
struct clknode *
317
clknode_find_by_name(const char *name)
318
{
319
struct clknode *entry;
320
321
CLK_TOPO_ASSERT();
322
323
TAILQ_FOREACH(entry, &clknode_list, clklist_link) {
324
if (strcmp(entry->name, name) == 0)
325
return (entry);
326
}
327
return (NULL);
328
}
329
330
struct clknode *
331
clknode_find_by_id(struct clkdom *clkdom, intptr_t id)
332
{
333
struct clknode *entry;
334
335
CLK_TOPO_ASSERT();
336
337
TAILQ_FOREACH(entry, &clkdom->clknode_list, clkdom_link) {
338
if (entry->id == id)
339
return (entry);
340
}
341
342
return (NULL);
343
}
344
345
/* -------------------------------------------------------------------------- */
346
/*
347
* Clock domain functions
348
*/
349
350
/* Find clock domain associated to device in global list. */
351
struct clkdom *
352
clkdom_get_by_dev(const device_t dev)
353
{
354
struct clkdom *entry;
355
356
CLK_TOPO_ASSERT();
357
358
TAILQ_FOREACH(entry, &clkdom_list, link) {
359
if (entry->dev == dev)
360
return (entry);
361
}
362
return (NULL);
363
}
364
365
366
#ifdef FDT
367
/* Default DT mapper. */
368
static int
369
clknode_default_ofw_map(struct clkdom *clkdom, uint32_t ncells,
370
phandle_t *cells, struct clknode **clk)
371
{
372
373
CLK_TOPO_ASSERT();
374
375
if (ncells == 0)
376
*clk = clknode_find_by_id(clkdom, 1);
377
else if (ncells == 1)
378
*clk = clknode_find_by_id(clkdom, cells[0]);
379
else
380
return (ERANGE);
381
382
if (*clk == NULL)
383
return (ENXIO);
384
return (0);
385
}
386
#endif
387
388
/*
389
* Create a clock domain. Returns with the topo lock held.
390
*/
391
struct clkdom *
392
clkdom_create(device_t dev)
393
{
394
struct clkdom *clkdom;
395
396
clkdom = malloc(sizeof(struct clkdom), M_CLOCK, M_WAITOK | M_ZERO);
397
clkdom->dev = dev;
398
TAILQ_INIT(&clkdom->clknode_list);
399
#ifdef FDT
400
clkdom->ofw_mapper = clknode_default_ofw_map;
401
#endif
402
403
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
404
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
405
OID_AUTO, "clocks",
406
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
407
clkdom, 0, clkdom_sysctl, "A",
408
"Clock list for the domain");
409
410
return (clkdom);
411
}
412
413
void
414
clkdom_unlock(struct clkdom *clkdom)
415
{
416
417
CLK_TOPO_UNLOCK();
418
}
419
420
void
421
clkdom_xlock(struct clkdom *clkdom)
422
{
423
424
CLK_TOPO_XLOCK();
425
}
426
427
/*
428
* Finalize initialization of clock domain. Releases topo lock.
429
*
430
* XXX Revisit failure handling.
431
*/
432
int
433
clkdom_finit(struct clkdom *clkdom)
434
{
435
struct clknode *clknode;
436
int i, rv;
437
#ifdef FDT
438
phandle_t node;
439
440
441
if ((node = ofw_bus_get_node(clkdom->dev)) == -1) {
442
device_printf(clkdom->dev,
443
"%s called on not ofw based device\n", __func__);
444
return (ENXIO);
445
}
446
#endif
447
rv = 0;
448
449
/* Make clock domain globally visible. */
450
CLK_TOPO_XLOCK();
451
TAILQ_INSERT_TAIL(&clkdom_list, clkdom, link);
452
#ifdef FDT
453
OF_device_register_xref(OF_xref_from_node(node), clkdom->dev);
454
#endif
455
456
/* Register all clock names into global list. */
457
TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
458
TAILQ_INSERT_TAIL(&clknode_list, clknode, clklist_link);
459
}
460
/*
461
* At this point all domain nodes must be registered and all
462
* parents must be valid.
463
*/
464
TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
465
if (clknode->parent_cnt == 0)
466
continue;
467
for (i = 0; i < clknode->parent_cnt; i++) {
468
if (clknode->parents[i] != NULL)
469
continue;
470
if (clknode->parent_names[i] == NULL)
471
continue;
472
clknode->parents[i] = clknode_find_by_name(
473
clknode->parent_names[i]);
474
if (clknode->parents[i] == NULL) {
475
device_printf(clkdom->dev,
476
"Clock %s have unknown parent: %s\n",
477
clknode->name, clknode->parent_names[i]);
478
rv = ENODEV;
479
}
480
}
481
482
/* If parent index is not set yet... */
483
if (clknode->parent_idx == CLKNODE_IDX_NONE) {
484
device_printf(clkdom->dev,
485
"Clock %s have not set parent idx\n",
486
clknode->name);
487
rv = ENXIO;
488
continue;
489
}
490
if (clknode->parents[clknode->parent_idx] == NULL) {
491
device_printf(clkdom->dev,
492
"Clock %s have unknown parent(idx %d): %s\n",
493
clknode->name, clknode->parent_idx,
494
clknode->parent_names[clknode->parent_idx]);
495
rv = ENXIO;
496
continue;
497
}
498
clknode_adjust_parent(clknode, clknode->parent_idx);
499
}
500
CLK_TOPO_UNLOCK();
501
return (rv);
502
}
503
504
/* Dump clock domain. */
505
void
506
clkdom_dump(struct clkdom * clkdom)
507
{
508
struct clknode *clknode;
509
int rv;
510
uint64_t freq;
511
512
CLK_TOPO_SLOCK();
513
TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
514
rv = clknode_get_freq(clknode, &freq);
515
if (rv != 0) {
516
printf("Clock: %s, error getting frequency: %d\n",
517
clknode->name, rv);
518
continue;
519
}
520
521
if (clknode->parent != NULL) {
522
printf("Clock: %s, parent: %s(%d), freq: %ju\n",
523
clknode->name, clknode->parent->name,
524
clknode->parent_idx, (uintmax_t)freq);
525
} else {
526
printf("Clock: %s, parent: none, freq: %ju\n",
527
clknode->name, (uintmax_t)freq);
528
}
529
}
530
CLK_TOPO_UNLOCK();
531
}
532
533
/*
534
* Create and initialize clock object, but do not register it.
535
*/
536
struct clknode *
537
clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
538
const struct clknode_init_def *def)
539
{
540
struct clknode *clknode;
541
struct sysctl_oid *clknode_oid;
542
bool replaced;
543
kobjop_desc_t kobj_desc;
544
kobj_method_t *kobj_method;
545
546
KASSERT(def->name != NULL, ("clock name is NULL"));
547
KASSERT(def->name[0] != '\0', ("clock name is empty"));
548
if (def->flags & CLK_NODE_LINKED) {
549
KASSERT(def->parent_cnt == 0,
550
("Linked clock must not have parents"));
551
KASSERT(clknode_class->size== 0,
552
("Linked clock cannot have own softc"));
553
}
554
555
/* Process duplicated clocks */
556
CLK_TOPO_SLOCK();
557
clknode = clknode_find_by_name(def->name);
558
CLK_TOPO_UNLOCK();
559
if (clknode != NULL) {
560
if (!(clknode->flags & CLK_NODE_LINKED) &&
561
def->flags & CLK_NODE_LINKED) {
562
/*
563
* New clock is linked and real already exists.
564
* Do nothing and return real node. It is in right
565
* domain, enqueued in right lists and fully initialized.
566
*/
567
return (clknode);
568
} else if (clknode->flags & CLK_NODE_LINKED &&
569
!(def->flags & CLK_NODE_LINKED)) {
570
/*
571
* New clock is real but linked already exists.
572
* Remove old linked node from originating domain
573
* (real clock must be owned by another) and from
574
* global names link (it will be added back into it
575
* again in following clknode_register()). Then reuse
576
* original clknode structure and reinitialize it
577
* with new dat. By this, all lists containing this
578
* node remains valid, but the new node virtually
579
* replace the linked one.
580
*/
581
KASSERT(clkdom != clknode->clkdom,
582
("linked clock must be from another "
583
"domain that real one"));
584
TAILQ_REMOVE(&clkdom->clknode_list, clknode,
585
clkdom_link);
586
TAILQ_REMOVE(&clknode_list, clknode, clklist_link);
587
replaced = true;
588
} else if (clknode->flags & CLK_NODE_LINKED &&
589
def->flags & CLK_NODE_LINKED) {
590
/*
591
* Both clocks are linked.
592
* Return old one, so we hold only one copy od link.
593
*/
594
return (clknode);
595
} else {
596
/* Both clocks are real */
597
panic("Duplicated clock registration: %s\n", def->name);
598
}
599
} else {
600
/* Create clknode object and initialize it. */
601
clknode = malloc(sizeof(struct clknode), M_CLOCK,
602
M_WAITOK | M_ZERO);
603
sx_init(&clknode->lock, "Clocknode lock");
604
TAILQ_INIT(&clknode->children);
605
replaced = false;
606
}
607
608
kobj_init((kobj_t)clknode, (kobj_class_t)clknode_class);
609
610
/* Allocate softc if required. */
611
if (clknode_class->size > 0) {
612
clknode->softc = malloc(clknode_class->size,
613
M_CLOCK, M_WAITOK | M_ZERO);
614
}
615
616
/* Prepare array for ptrs to parent clocks. */
617
clknode->parents = malloc(sizeof(struct clknode *) * def->parent_cnt,
618
M_CLOCK, M_WAITOK | M_ZERO);
619
620
/* Copy all strings unless they're flagged as static. */
621
if (def->flags & CLK_NODE_STATIC_STRINGS) {
622
clknode->name = def->name;
623
clknode->parent_names = def->parent_names;
624
} else {
625
clknode->name = strdup(def->name, M_CLOCK);
626
clknode->parent_names =
627
strdup_list(def->parent_names, def->parent_cnt);
628
}
629
630
/* Rest of init. */
631
clknode->id = def->id;
632
clknode->clkdom = clkdom;
633
clknode->flags = def->flags;
634
clknode->parent_cnt = def->parent_cnt;
635
clknode->parent = NULL;
636
clknode->parent_idx = CLKNODE_IDX_NONE;
637
638
if (replaced)
639
return (clknode);
640
641
sysctl_ctx_init(&clknode->sysctl_ctx);
642
clknode_oid = SYSCTL_ADD_NODE(&clknode->sysctl_ctx,
643
SYSCTL_STATIC_CHILDREN(_hw_clock),
644
OID_AUTO, clknode->name,
645
CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "A clock node");
646
647
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
648
SYSCTL_CHILDREN(clknode_oid),
649
OID_AUTO, "frequency",
650
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
651
clknode, CLKNODE_SYSCTL_FREQUENCY, clknode_sysctl,
652
"A",
653
"The clock frequency");
654
655
/* Install gate handler only if clknode have 'set_gate' method */
656
kobj_desc = &clknode_set_gate_desc;
657
kobj_method = kobj_lookup_method(((kobj_t)clknode)->ops->cls, NULL,
658
kobj_desc);
659
if (kobj_method != &kobj_desc->deflt &&
660
kobj_method->func != (kobjop_t)clknode_method_set_gate) {
661
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
662
SYSCTL_CHILDREN(clknode_oid),
663
OID_AUTO, "gate",
664
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
665
clknode, CLKNODE_SYSCTL_GATE, clknode_sysctl,
666
"A",
667
"The clock gate status");
668
}
669
670
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
671
SYSCTL_CHILDREN(clknode_oid),
672
OID_AUTO, "parent",
673
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
674
clknode, CLKNODE_SYSCTL_PARENT, clknode_sysctl,
675
"A",
676
"The clock parent");
677
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
678
SYSCTL_CHILDREN(clknode_oid),
679
OID_AUTO, "parents",
680
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
681
clknode, CLKNODE_SYSCTL_PARENTS_LIST, clknode_sysctl,
682
"A",
683
"The clock parents list");
684
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
685
SYSCTL_CHILDREN(clknode_oid),
686
OID_AUTO, "childrens",
687
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
688
clknode, CLKNODE_SYSCTL_CHILDREN_LIST, clknode_sysctl,
689
"A",
690
"The clock childrens list");
691
SYSCTL_ADD_INT(&clknode->sysctl_ctx,
692
SYSCTL_CHILDREN(clknode_oid),
693
OID_AUTO, "enable_cnt",
694
CTLFLAG_RD, &clknode->enable_cnt, 0, "The clock enable counter");
695
696
return (clknode);
697
}
698
699
/*
700
* Register clock object into clock domain hierarchy.
701
*/
702
struct clknode *
703
clknode_register(struct clkdom * clkdom, struct clknode *clknode)
704
{
705
int rv;
706
707
/* Skip already registered linked node */
708
if (clknode->flags & CLK_NODE_REGISTERED)
709
return(clknode);
710
711
rv = CLKNODE_INIT(clknode, clknode_get_device(clknode));
712
if (rv != 0) {
713
printf(" CLKNODE_INIT failed: %d\n", rv);
714
return (NULL);
715
}
716
717
TAILQ_INSERT_TAIL(&clkdom->clknode_list, clknode, clkdom_link);
718
clknode->flags |= CLK_NODE_REGISTERED;
719
return (clknode);
720
}
721
722
723
static void
724
clknode_finish(void *dummy)
725
{
726
struct clknode *clknode;
727
728
CLK_TOPO_SLOCK();
729
TAILQ_FOREACH(clknode, &clknode_list, clklist_link) {
730
if (clknode->flags & CLK_NODE_LINKED)
731
printf("Unresolved linked clock found: %s\n",
732
clknode->name);
733
}
734
CLK_TOPO_UNLOCK();
735
}
736
/*
737
* Clock providers interface.
738
*/
739
740
/*
741
* Reparent clock node.
742
*/
743
static void
744
clknode_adjust_parent(struct clknode *clknode, int idx)
745
{
746
747
CLK_TOPO_XASSERT();
748
749
if (clknode->parent_cnt == 0)
750
return;
751
if ((idx == CLKNODE_IDX_NONE) || (idx >= clknode->parent_cnt))
752
panic("%s: Invalid parent index %d for clock %s",
753
__func__, idx, clknode->name);
754
755
if (clknode->parents[idx] == NULL)
756
panic("%s: Invalid parent index %d for clock %s",
757
__func__, idx, clknode->name);
758
759
/* Remove me from old children list. */
760
if (clknode->parent != NULL) {
761
TAILQ_REMOVE(&clknode->parent->children, clknode, sibling_link);
762
}
763
764
/* Insert into children list of new parent. */
765
clknode->parent_idx = idx;
766
clknode->parent = clknode->parents[idx];
767
TAILQ_INSERT_TAIL(&clknode->parent->children, clknode, sibling_link);
768
}
769
770
/*
771
* Set parent index - init function.
772
*/
773
void
774
clknode_init_parent_idx(struct clknode *clknode, int idx)
775
{
776
777
if (clknode->parent_cnt == 0) {
778
clknode->parent_idx = CLKNODE_IDX_NONE;
779
clknode->parent = NULL;
780
return;
781
}
782
if ((idx == CLKNODE_IDX_NONE) ||
783
(idx >= clknode->parent_cnt) ||
784
(clknode->parent_names[idx] == NULL))
785
panic("%s: Invalid parent index %d for clock %s",
786
__func__, idx, clknode->name);
787
clknode->parent_idx = idx;
788
}
789
790
int
791
clknode_set_parent_by_idx(struct clknode *clknode, int idx)
792
{
793
int rv;
794
uint64_t freq;
795
int oldidx;
796
797
/* We have exclusive topology lock, node lock is not needed. */
798
CLK_TOPO_XASSERT();
799
800
if (clknode->parent_cnt == 0)
801
return (0);
802
803
if (clknode->parent_idx == idx)
804
return (0);
805
806
oldidx = clknode->parent_idx;
807
clknode_adjust_parent(clknode, idx);
808
rv = CLKNODE_SET_MUX(clknode, idx);
809
if (rv != 0) {
810
clknode_adjust_parent(clknode, oldidx);
811
return (rv);
812
}
813
rv = clknode_get_freq(clknode->parent, &freq);
814
if (rv != 0)
815
return (rv);
816
rv = clknode_refresh_cache(clknode, freq);
817
return (rv);
818
}
819
820
int
821
clknode_set_parent_by_name(struct clknode *clknode, const char *name)
822
{
823
int rv;
824
uint64_t freq;
825
int oldidx, idx;
826
827
/* We have exclusive topology lock, node lock is not needed. */
828
CLK_TOPO_XASSERT();
829
830
if (clknode->parent_cnt == 0)
831
return (0);
832
833
/*
834
* If this node doesnt have mux, then passthrough request to parent.
835
* This feature is used in clock domain initialization and allows us to
836
* set clock source and target frequency on the tail node of the clock
837
* chain.
838
*/
839
if (clknode->parent_cnt == 1) {
840
rv = clknode_set_parent_by_name(clknode->parent, name);
841
return (rv);
842
}
843
844
for (idx = 0; idx < clknode->parent_cnt; idx++) {
845
if (clknode->parent_names[idx] == NULL)
846
continue;
847
if (strcmp(clknode->parent_names[idx], name) == 0)
848
break;
849
}
850
if (idx >= clknode->parent_cnt) {
851
return (ENXIO);
852
}
853
if (clknode->parent_idx == idx)
854
return (0);
855
856
oldidx = clknode->parent_idx;
857
clknode_adjust_parent(clknode, idx);
858
rv = CLKNODE_SET_MUX(clknode, idx);
859
if (rv != 0) {
860
clknode_adjust_parent(clknode, oldidx);
861
CLKNODE_UNLOCK(clknode);
862
return (rv);
863
}
864
rv = clknode_get_freq(clknode->parent, &freq);
865
if (rv != 0)
866
return (rv);
867
rv = clknode_refresh_cache(clknode, freq);
868
return (rv);
869
}
870
871
struct clknode *
872
clknode_get_parent(struct clknode *clknode)
873
{
874
875
return (clknode->parent);
876
}
877
878
const char *
879
clknode_get_name(struct clknode *clknode)
880
{
881
882
return (clknode->name);
883
}
884
885
const char **
886
clknode_get_parent_names(struct clknode *clknode)
887
{
888
889
return (clknode->parent_names);
890
}
891
892
int
893
clknode_get_parents_num(struct clknode *clknode)
894
{
895
896
return (clknode->parent_cnt);
897
}
898
899
int
900
clknode_get_parent_idx(struct clknode *clknode)
901
{
902
903
return (clknode->parent_idx);
904
}
905
906
int
907
clknode_get_flags(struct clknode *clknode)
908
{
909
910
return (clknode->flags);
911
}
912
913
914
void *
915
clknode_get_softc(struct clknode *clknode)
916
{
917
918
return (clknode->softc);
919
}
920
921
device_t
922
clknode_get_device(struct clknode *clknode)
923
{
924
925
return (clknode->clkdom->dev);
926
}
927
928
#ifdef FDT
929
void
930
clkdom_set_ofw_mapper(struct clkdom * clkdom, clknode_ofw_mapper_func *map)
931
{
932
933
clkdom->ofw_mapper = map;
934
}
935
#endif
936
937
/*
938
* Real consumers executive
939
*/
940
int
941
clknode_get_freq(struct clknode *clknode, uint64_t *freq)
942
{
943
int rv;
944
945
CLK_TOPO_ASSERT();
946
947
/* Use cached value, if it exists. */
948
*freq = clknode->freq;
949
if (*freq != 0)
950
return (0);
951
952
/* Get frequency from parent, if the clock has a parent. */
953
if (clknode->parent_cnt > 0) {
954
rv = clknode_get_freq(clknode->parent, freq);
955
if (rv != 0) {
956
return (rv);
957
}
958
}
959
960
/* And recalculate my output frequency. */
961
CLKNODE_XLOCK(clknode);
962
rv = CLKNODE_RECALC_FREQ(clknode, freq);
963
if (rv != 0) {
964
CLKNODE_UNLOCK(clknode);
965
printf("Cannot get frequency for clk: %s, error: %d\n",
966
clknode->name, rv);
967
return (rv);
968
}
969
970
/* Save new frequency to cache. */
971
clknode->freq = *freq;
972
CLKNODE_UNLOCK(clknode);
973
return (0);
974
}
975
976
static int
977
_clknode_set_freq(struct clknode *clknode, uint64_t *freq, int flags,
978
int enablecnt)
979
{
980
int rv, done;
981
uint64_t parent_freq;
982
983
/* We have exclusive topology lock, node lock is not needed. */
984
CLK_TOPO_XASSERT();
985
986
/* Check for no change */
987
if (clknode->freq == *freq)
988
return (0);
989
990
parent_freq = 0;
991
992
/*
993
* We can set frequency only if
994
* clock is disabled
995
* OR
996
* clock is glitch free and is enabled by calling consumer only
997
*/
998
if ((flags & CLK_SET_DRYRUN) == 0 &&
999
clknode->enable_cnt > 1 &&
1000
clknode->enable_cnt > enablecnt &&
1001
(clknode->flags & CLK_NODE_GLITCH_FREE) == 0) {
1002
return (EBUSY);
1003
}
1004
1005
/* Get frequency from parent, if the clock has a parent. */
1006
if (clknode->parent_cnt > 0) {
1007
rv = clknode_get_freq(clknode->parent, &parent_freq);
1008
if (rv != 0) {
1009
return (rv);
1010
}
1011
}
1012
1013
/* Set frequency for this clock. */
1014
rv = CLKNODE_SET_FREQ(clknode, parent_freq, freq, flags, &done);
1015
if (rv != 0) {
1016
printf("Cannot set frequency for clk: %s, error: %d\n",
1017
clknode->name, rv);
1018
if ((flags & CLK_SET_DRYRUN) == 0)
1019
clknode_refresh_cache(clknode, parent_freq);
1020
return (rv);
1021
}
1022
1023
if (done) {
1024
/* Success - invalidate frequency cache for all children. */
1025
if ((flags & CLK_SET_DRYRUN) == 0) {
1026
clknode->freq = *freq;
1027
/* Clock might have reparent during set_freq */
1028
if (clknode->parent_cnt > 0) {
1029
rv = clknode_get_freq(clknode->parent,
1030
&parent_freq);
1031
if (rv != 0) {
1032
return (rv);
1033
}
1034
}
1035
clknode_refresh_cache(clknode, parent_freq);
1036
}
1037
} else if (clknode->parent != NULL) {
1038
/* Nothing changed, pass request to parent. */
1039
rv = _clknode_set_freq(clknode->parent, freq, flags,
1040
enablecnt);
1041
} else {
1042
/* End of chain without action. */
1043
printf("Cannot set frequency for clk: %s, end of chain\n",
1044
clknode->name);
1045
rv = ENXIO;
1046
}
1047
1048
return (rv);
1049
}
1050
1051
int
1052
clknode_set_freq(struct clknode *clknode, uint64_t freq, int flags,
1053
int enablecnt)
1054
{
1055
1056
return (_clknode_set_freq(clknode, &freq, flags, enablecnt));
1057
}
1058
1059
int
1060
clknode_test_freq(struct clknode *clknode, uint64_t freq, int flags,
1061
int enablecnt, uint64_t *out_freq)
1062
{
1063
int rv;
1064
1065
rv = _clknode_set_freq(clknode, &freq, flags | CLK_SET_DRYRUN,
1066
enablecnt);
1067
if (out_freq != NULL)
1068
*out_freq = freq;
1069
1070
return (rv);
1071
}
1072
1073
int
1074
clknode_enable(struct clknode *clknode)
1075
{
1076
int rv;
1077
1078
CLK_TOPO_ASSERT();
1079
1080
/* Enable clock for each node in chain, starting from source. */
1081
if (clknode->parent_cnt > 0) {
1082
rv = clknode_enable(clknode->parent);
1083
if (rv != 0) {
1084
return (rv);
1085
}
1086
}
1087
1088
/* Handle this node */
1089
CLKNODE_XLOCK(clknode);
1090
if (clknode->enable_cnt == 0) {
1091
rv = CLKNODE_SET_GATE(clknode, 1);
1092
if (rv != 0) {
1093
CLKNODE_UNLOCK(clknode);
1094
return (rv);
1095
}
1096
}
1097
clknode->enable_cnt++;
1098
CLKNODE_UNLOCK(clknode);
1099
return (0);
1100
}
1101
1102
int
1103
clknode_disable(struct clknode *clknode)
1104
{
1105
int rv;
1106
1107
CLK_TOPO_ASSERT();
1108
rv = 0;
1109
1110
CLKNODE_XLOCK(clknode);
1111
/* Disable clock for each node in chain, starting from consumer. */
1112
if ((clknode->enable_cnt == 1) &&
1113
((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
1114
rv = CLKNODE_SET_GATE(clknode, 0);
1115
if (rv != 0) {
1116
CLKNODE_UNLOCK(clknode);
1117
return (rv);
1118
}
1119
}
1120
clknode->enable_cnt--;
1121
CLKNODE_UNLOCK(clknode);
1122
1123
if (clknode->parent_cnt > 0) {
1124
rv = clknode_disable(clknode->parent);
1125
}
1126
return (rv);
1127
}
1128
1129
int
1130
clknode_stop(struct clknode *clknode, int depth)
1131
{
1132
int rv;
1133
1134
CLK_TOPO_ASSERT();
1135
rv = 0;
1136
1137
CLKNODE_XLOCK(clknode);
1138
/* The first node cannot be enabled. */
1139
if ((clknode->enable_cnt != 0) && (depth == 0)) {
1140
CLKNODE_UNLOCK(clknode);
1141
return (EBUSY);
1142
}
1143
/* Stop clock for each node in chain, starting from consumer. */
1144
if ((clknode->enable_cnt == 0) &&
1145
((clknode->flags & CLK_NODE_CANNOT_STOP) == 0)) {
1146
rv = CLKNODE_SET_GATE(clknode, 0);
1147
if (rv != 0) {
1148
CLKNODE_UNLOCK(clknode);
1149
return (rv);
1150
}
1151
}
1152
CLKNODE_UNLOCK(clknode);
1153
1154
if (clknode->parent_cnt > 0)
1155
rv = clknode_stop(clknode->parent, depth + 1);
1156
return (rv);
1157
}
1158
1159
/* --------------------------------------------------------------------------
1160
*
1161
* Clock consumers interface.
1162
*
1163
*/
1164
/* Helper function for clk_get*() */
1165
static clk_t
1166
clk_create(struct clknode *clknode, device_t dev)
1167
{
1168
struct clk *clk;
1169
1170
CLK_TOPO_ASSERT();
1171
1172
clk = malloc(sizeof(struct clk), M_CLOCK, M_WAITOK);
1173
clk->dev = dev;
1174
clk->clknode = clknode;
1175
clk->enable_cnt = 0;
1176
clknode->ref_cnt++;
1177
1178
return (clk);
1179
}
1180
1181
int
1182
clk_get_freq(clk_t clk, uint64_t *freq)
1183
{
1184
int rv;
1185
struct clknode *clknode;
1186
1187
clknode = clk->clknode;
1188
KASSERT(clknode->ref_cnt > 0,
1189
("Attempt to access unreferenced clock: %s\n", clknode->name));
1190
1191
CLK_TOPO_SLOCK();
1192
rv = clknode_get_freq(clknode, freq);
1193
CLK_TOPO_UNLOCK();
1194
return (rv);
1195
}
1196
1197
int
1198
clk_set_freq(clk_t clk, uint64_t freq, int flags)
1199
{
1200
int rv;
1201
struct clknode *clknode;
1202
1203
flags &= CLK_SET_USER_MASK;
1204
clknode = clk->clknode;
1205
KASSERT(clknode->ref_cnt > 0,
1206
("Attempt to access unreferenced clock: %s\n", clknode->name));
1207
1208
CLK_TOPO_XLOCK();
1209
rv = clknode_set_freq(clknode, freq, flags, clk->enable_cnt);
1210
CLK_TOPO_UNLOCK();
1211
return (rv);
1212
}
1213
1214
int
1215
clk_test_freq(clk_t clk, uint64_t freq, int flags)
1216
{
1217
int rv;
1218
struct clknode *clknode;
1219
1220
flags &= CLK_SET_USER_MASK;
1221
clknode = clk->clknode;
1222
KASSERT(clknode->ref_cnt > 0,
1223
("Attempt to access unreferenced clock: %s\n", clknode->name));
1224
1225
CLK_TOPO_XLOCK();
1226
rv = clknode_set_freq(clknode, freq, flags | CLK_SET_DRYRUN, 0);
1227
CLK_TOPO_UNLOCK();
1228
return (rv);
1229
}
1230
1231
int
1232
clk_get_parent(clk_t clk, clk_t *parent)
1233
{
1234
struct clknode *clknode;
1235
struct clknode *parentnode;
1236
1237
clknode = clk->clknode;
1238
KASSERT(clknode->ref_cnt > 0,
1239
("Attempt to access unreferenced clock: %s\n", clknode->name));
1240
1241
CLK_TOPO_SLOCK();
1242
parentnode = clknode_get_parent(clknode);
1243
if (parentnode == NULL) {
1244
CLK_TOPO_UNLOCK();
1245
return (ENODEV);
1246
}
1247
*parent = clk_create(parentnode, clk->dev);
1248
CLK_TOPO_UNLOCK();
1249
return (0);
1250
}
1251
1252
int
1253
clk_set_parent_by_clk(clk_t clk, clk_t parent)
1254
{
1255
int rv;
1256
struct clknode *clknode;
1257
struct clknode *parentnode;
1258
1259
clknode = clk->clknode;
1260
parentnode = parent->clknode;
1261
KASSERT(clknode->ref_cnt > 0,
1262
("Attempt to access unreferenced clock: %s\n", clknode->name));
1263
KASSERT(parentnode->ref_cnt > 0,
1264
("Attempt to access unreferenced clock: %s\n", clknode->name));
1265
CLK_TOPO_XLOCK();
1266
rv = clknode_set_parent_by_name(clknode, parentnode->name);
1267
CLK_TOPO_UNLOCK();
1268
return (rv);
1269
}
1270
1271
int
1272
clk_enable(clk_t clk)
1273
{
1274
int rv;
1275
struct clknode *clknode;
1276
1277
clknode = clk->clknode;
1278
KASSERT(clknode->ref_cnt > 0,
1279
("Attempt to access unreferenced clock: %s\n", clknode->name));
1280
CLK_TOPO_SLOCK();
1281
rv = clknode_enable(clknode);
1282
if (rv == 0)
1283
clk->enable_cnt++;
1284
CLK_TOPO_UNLOCK();
1285
return (rv);
1286
}
1287
1288
int
1289
clk_disable(clk_t clk)
1290
{
1291
int rv;
1292
struct clknode *clknode;
1293
1294
clknode = clk->clknode;
1295
KASSERT(clknode->ref_cnt > 0,
1296
("Attempt to access unreferenced clock: %s\n", clknode->name));
1297
KASSERT(clk->enable_cnt > 0,
1298
("Attempt to disable already disabled clock: %s\n", clknode->name));
1299
CLK_TOPO_SLOCK();
1300
rv = clknode_disable(clknode);
1301
if (rv == 0)
1302
clk->enable_cnt--;
1303
CLK_TOPO_UNLOCK();
1304
return (rv);
1305
}
1306
1307
int
1308
clk_stop(clk_t clk)
1309
{
1310
int rv;
1311
struct clknode *clknode;
1312
1313
clknode = clk->clknode;
1314
KASSERT(clknode->ref_cnt > 0,
1315
("Attempt to access unreferenced clock: %s\n", clknode->name));
1316
KASSERT(clk->enable_cnt == 0,
1317
("Attempt to stop already enabled clock: %s\n", clknode->name));
1318
1319
CLK_TOPO_SLOCK();
1320
rv = clknode_stop(clknode, 0);
1321
CLK_TOPO_UNLOCK();
1322
return (rv);
1323
}
1324
1325
int
1326
clk_release(clk_t clk)
1327
{
1328
struct clknode *clknode;
1329
1330
clknode = clk->clknode;
1331
KASSERT(clknode->ref_cnt > 0,
1332
("Attempt to access unreferenced clock: %s\n", clknode->name));
1333
CLK_TOPO_SLOCK();
1334
while (clk->enable_cnt > 0) {
1335
clknode_disable(clknode);
1336
clk->enable_cnt--;
1337
}
1338
CLKNODE_XLOCK(clknode);
1339
clknode->ref_cnt--;
1340
CLKNODE_UNLOCK(clknode);
1341
CLK_TOPO_UNLOCK();
1342
1343
free(clk, M_CLOCK);
1344
return (0);
1345
}
1346
1347
const char *
1348
clk_get_name(clk_t clk)
1349
{
1350
const char *name;
1351
struct clknode *clknode;
1352
1353
clknode = clk->clknode;
1354
KASSERT(clknode->ref_cnt > 0,
1355
("Attempt to access unreferenced clock: %s\n", clknode->name));
1356
name = clknode_get_name(clknode);
1357
return (name);
1358
}
1359
1360
int
1361
clk_get_by_name(device_t dev, const char *name, clk_t *clk)
1362
{
1363
struct clknode *clknode;
1364
1365
CLK_TOPO_SLOCK();
1366
clknode = clknode_find_by_name(name);
1367
if (clknode == NULL) {
1368
CLK_TOPO_UNLOCK();
1369
return (ENODEV);
1370
}
1371
*clk = clk_create(clknode, dev);
1372
CLK_TOPO_UNLOCK();
1373
return (0);
1374
}
1375
1376
int
1377
clk_get_by_id(device_t dev, struct clkdom *clkdom, intptr_t id, clk_t *clk)
1378
{
1379
struct clknode *clknode;
1380
1381
CLK_TOPO_SLOCK();
1382
1383
clknode = clknode_find_by_id(clkdom, id);
1384
if (clknode == NULL) {
1385
CLK_TOPO_UNLOCK();
1386
return (ENODEV);
1387
}
1388
*clk = clk_create(clknode, dev);
1389
CLK_TOPO_UNLOCK();
1390
1391
return (0);
1392
}
1393
1394
#ifdef FDT
1395
1396
static void
1397
clk_set_assigned_parent(device_t dev, clk_t clk, int idx)
1398
{
1399
clk_t parent;
1400
const char *pname;
1401
int rv;
1402
1403
rv = clk_get_by_ofw_index_prop(dev, 0,
1404
"assigned-clock-parents", idx, &parent);
1405
if (rv != 0) {
1406
device_printf(dev,
1407
"cannot get parent at idx %d\n", idx);
1408
return;
1409
}
1410
1411
pname = clk_get_name(parent);
1412
rv = clk_set_parent_by_clk(clk, parent);
1413
if (rv != 0)
1414
device_printf(dev,
1415
"Cannot set parent %s for clock %s\n",
1416
pname, clk_get_name(clk));
1417
else if (bootverbose)
1418
device_printf(dev, "Set %s as the parent of %s\n",
1419
pname, clk_get_name(clk));
1420
clk_release(parent);
1421
}
1422
1423
static void
1424
clk_set_assigned_rates(device_t dev, clk_t clk, uint32_t freq)
1425
{
1426
int rv;
1427
1428
rv = clk_set_freq(clk, freq, CLK_SET_ROUND_DOWN | CLK_SET_ROUND_UP);
1429
if (rv != 0) {
1430
device_printf(dev, "Failed to set %s to a frequency of %u\n",
1431
clk_get_name(clk), freq);
1432
return;
1433
}
1434
if (bootverbose)
1435
device_printf(dev, "Set %s to %u\n",
1436
clk_get_name(clk), freq);
1437
}
1438
1439
int
1440
clk_set_assigned(device_t dev, phandle_t node)
1441
{
1442
clk_t clk;
1443
uint32_t *rates;
1444
int rv, nclocks, nrates, nparents, i;
1445
1446
rv = ofw_bus_parse_xref_list_get_length(node,
1447
"assigned-clocks", "#clock-cells", &nclocks);
1448
1449
if (rv != 0) {
1450
if (rv != ENOENT)
1451
device_printf(dev,
1452
"cannot parse assigned-clock property\n");
1453
return (rv);
1454
}
1455
1456
nrates = OF_getencprop_alloc_multi(node, "assigned-clock-rates",
1457
sizeof(*rates), (void **)&rates);
1458
if (nrates <= 0)
1459
nrates = 0;
1460
1461
if (ofw_bus_parse_xref_list_get_length(node,
1462
"assigned-clock-parents", "#clock-cells", &nparents) != 0)
1463
nparents = -1;
1464
for (i = 0; i < nclocks; i++) {
1465
/* First get the clock we are supposed to modify */
1466
rv = clk_get_by_ofw_index_prop(dev, 0, "assigned-clocks",
1467
i, &clk);
1468
if (rv != 0) {
1469
if (bootverbose)
1470
device_printf(dev,
1471
"cannot get assigned clock at idx %d\n",
1472
i);
1473
continue;
1474
}
1475
1476
/* First set it's parent if needed */
1477
if (i < nparents)
1478
clk_set_assigned_parent(dev, clk, i);
1479
1480
/* Then set a new frequency */
1481
if (i < nrates && rates[i] != 0)
1482
clk_set_assigned_rates(dev, clk, rates[i]);
1483
1484
clk_release(clk);
1485
}
1486
if (rates != NULL)
1487
OF_prop_free(rates);
1488
1489
return (0);
1490
}
1491
1492
int
1493
clk_get_by_ofw_index_prop(device_t dev, phandle_t cnode, const char *prop, int idx, clk_t *clk)
1494
{
1495
phandle_t parent, *cells;
1496
device_t clockdev;
1497
int ncells, rv;
1498
struct clkdom *clkdom;
1499
struct clknode *clknode;
1500
1501
*clk = NULL;
1502
if (cnode <= 0)
1503
cnode = ofw_bus_get_node(dev);
1504
if (cnode <= 0) {
1505
device_printf(dev, "%s called on not ofw based device\n",
1506
__func__);
1507
return (ENXIO);
1508
}
1509
1510
1511
rv = ofw_bus_parse_xref_list_alloc(cnode, prop, "#clock-cells", idx,
1512
&parent, &ncells, &cells);
1513
if (rv != 0) {
1514
return (rv);
1515
}
1516
1517
clockdev = OF_device_from_xref(parent);
1518
if (clockdev == NULL) {
1519
rv = ENODEV;
1520
goto done;
1521
}
1522
1523
CLK_TOPO_SLOCK();
1524
clkdom = clkdom_get_by_dev(clockdev);
1525
if (clkdom == NULL){
1526
CLK_TOPO_UNLOCK();
1527
rv = ENXIO;
1528
goto done;
1529
}
1530
1531
rv = clkdom->ofw_mapper(clkdom, ncells, cells, &clknode);
1532
if (rv == 0) {
1533
*clk = clk_create(clknode, dev);
1534
}
1535
CLK_TOPO_UNLOCK();
1536
1537
done:
1538
if (cells != NULL)
1539
OF_prop_free(cells);
1540
return (rv);
1541
}
1542
1543
int
1544
clk_get_by_ofw_index(device_t dev, phandle_t cnode, int idx, clk_t *clk)
1545
{
1546
return (clk_get_by_ofw_index_prop(dev, cnode, "clocks", idx, clk));
1547
}
1548
1549
int
1550
clk_get_by_ofw_name(device_t dev, phandle_t cnode, const char *name, clk_t *clk)
1551
{
1552
int rv, idx;
1553
1554
if (cnode <= 0)
1555
cnode = ofw_bus_get_node(dev);
1556
if (cnode <= 0) {
1557
device_printf(dev, "%s called on not ofw based device\n",
1558
__func__);
1559
return (ENXIO);
1560
}
1561
rv = ofw_bus_find_string_index(cnode, "clock-names", name, &idx);
1562
if (rv != 0)
1563
return (rv);
1564
return (clk_get_by_ofw_index(dev, cnode, idx, clk));
1565
}
1566
1567
/* --------------------------------------------------------------------------
1568
*
1569
* Support functions for parsing various clock related OFW things.
1570
*/
1571
1572
/*
1573
* Get "clock-output-names" and (optional) "clock-indices" lists.
1574
* Both lists are allocated using M_OFWPROP specifier.
1575
*
1576
* Returns number of items or 0.
1577
*/
1578
int
1579
clk_parse_ofw_out_names(device_t dev, phandle_t node, const char ***out_names,
1580
uint32_t **indices)
1581
{
1582
int name_items, rv;
1583
1584
*out_names = NULL;
1585
*indices = NULL;
1586
if (!OF_hasprop(node, "clock-output-names"))
1587
return (0);
1588
rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1589
out_names);
1590
if (rv <= 0)
1591
return (0);
1592
name_items = rv;
1593
1594
if (!OF_hasprop(node, "clock-indices"))
1595
return (name_items);
1596
rv = OF_getencprop_alloc_multi(node, "clock-indices", sizeof (uint32_t),
1597
(void **)indices);
1598
if (rv != name_items) {
1599
device_printf(dev, " Size of 'clock-output-names' and "
1600
"'clock-indices' differs\n");
1601
OF_prop_free(*out_names);
1602
OF_prop_free(*indices);
1603
return (0);
1604
}
1605
return (name_items);
1606
}
1607
1608
/*
1609
* Get output clock name for single output clock node.
1610
*/
1611
int
1612
clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name)
1613
{
1614
const char **out_names;
1615
const char *tmp_name;
1616
int rv;
1617
1618
*name = NULL;
1619
if (!OF_hasprop(node, "clock-output-names")) {
1620
tmp_name = ofw_bus_get_name(dev);
1621
if (tmp_name == NULL)
1622
return (ENXIO);
1623
*name = strdup(tmp_name, M_OFWPROP);
1624
return (0);
1625
}
1626
rv = ofw_bus_string_list_to_array(node, "clock-output-names",
1627
&out_names);
1628
if (rv != 1) {
1629
OF_prop_free(out_names);
1630
device_printf(dev, "Malformed 'clock-output-names' property\n");
1631
return (ENXIO);
1632
}
1633
*name = strdup(out_names[0], M_OFWPROP);
1634
OF_prop_free(out_names);
1635
return (0);
1636
}
1637
#endif
1638
1639
static int
1640
clkdom_sysctl(SYSCTL_HANDLER_ARGS)
1641
{
1642
struct clkdom *clkdom = arg1;
1643
struct clknode *clknode;
1644
struct sbuf *sb;
1645
int ret;
1646
1647
sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
1648
if (sb == NULL)
1649
return (ENOMEM);
1650
1651
CLK_TOPO_SLOCK();
1652
TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
1653
sbuf_printf(sb, "%s ", clknode->name);
1654
}
1655
CLK_TOPO_UNLOCK();
1656
1657
ret = sbuf_finish(sb);
1658
sbuf_delete(sb);
1659
return (ret);
1660
}
1661
1662
static int
1663
clknode_sysctl(SYSCTL_HANDLER_ARGS)
1664
{
1665
struct clknode *clknode, *children;
1666
enum clknode_sysctl_type type = arg2;
1667
struct sbuf *sb;
1668
const char **parent_names;
1669
uint64_t freq;
1670
bool enable;
1671
int ret, i;
1672
1673
clknode = arg1;
1674
sb = sbuf_new_for_sysctl(NULL, NULL, 512, req);
1675
if (sb == NULL)
1676
return (ENOMEM);
1677
1678
CLK_TOPO_SLOCK();
1679
switch (type) {
1680
case CLKNODE_SYSCTL_PARENT:
1681
if (clknode->parent)
1682
sbuf_printf(sb, "%s", clknode->parent->name);
1683
break;
1684
case CLKNODE_SYSCTL_PARENTS_LIST:
1685
parent_names = clknode_get_parent_names(clknode);
1686
for (i = 0; i < clknode->parent_cnt; i++)
1687
sbuf_printf(sb, "%s ", parent_names[i]);
1688
break;
1689
case CLKNODE_SYSCTL_CHILDREN_LIST:
1690
TAILQ_FOREACH(children, &(clknode->children), sibling_link) {
1691
sbuf_printf(sb, "%s ", children->name);
1692
}
1693
break;
1694
case CLKNODE_SYSCTL_FREQUENCY:
1695
ret = clknode_get_freq(clknode, &freq);
1696
if (ret == 0)
1697
sbuf_printf(sb, "%ju ", (uintmax_t)freq);
1698
else
1699
sbuf_printf(sb, "Error: %d ", ret);
1700
break;
1701
case CLKNODE_SYSCTL_GATE:
1702
ret = CLKNODE_GET_GATE(clknode, &enable);
1703
if (ret == 0)
1704
sbuf_printf(sb, enable ? "enabled": "disabled");
1705
else if (ret == ENXIO)
1706
sbuf_printf(sb, "unimplemented");
1707
else if (ret == ENOENT)
1708
sbuf_printf(sb, "unreadable");
1709
else
1710
sbuf_printf(sb, "Error: %d ", ret);
1711
break;
1712
}
1713
CLK_TOPO_UNLOCK();
1714
1715
ret = sbuf_finish(sb);
1716
sbuf_delete(sb);
1717
return (ret);
1718
}
1719
1720