Export a sysctl dev.<clkdom>.<unit>.clocks for each clock domain containing

all the clocks that they provide.
Each clocks are exported under the node 'clock.<clkname>' and have the following
children nodes :
- frequency
- parent (The selected parent, if any)
- parents (The list of parents, if any)
- childrens (The list of childrens, if any)
- enable_cnt (The enabled counter)

This give us the possibility to examine clocks at runtime and make graph of
the clock flow.

Reviewed by:	mmel
MFC after:	2 month
Differential Revision:	https://reviews.freebsd.org/D9833
This commit is contained in:
Emmanuel Vadot 2017-03-05 07:13:29 +00:00
parent 9edf60b1ed
commit c1b014c51c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=314699
3 changed files with 126 additions and 0 deletions

View File

@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/sx.h>
@ -118,6 +119,8 @@ struct clknode {
/* Cached values. */
uint64_t freq; /* Actual frequency */
struct sysctl_ctx_list sysctl_ctx;
};
/*
@ -175,6 +178,15 @@ SX_SYSINIT(clock_topology, &clk_topo_lock, "Clock topology lock");
static void clknode_adjust_parent(struct clknode *clknode, int idx);
enum clknode_sysctl_type {
CLKNODE_SYSCTL_PARENT,
CLKNODE_SYSCTL_PARENTS_LIST,
CLKNODE_SYSCTL_CHILDREN_LIST,
};
static int clknode_sysctl(SYSCTL_HANDLER_ARGS);
static int clkdom_sysctl(SYSCTL_HANDLER_ARGS);
/*
* Default clock methods for base class.
*/
@ -382,6 +394,14 @@ clkdom_create(device_t dev)
clkdom->ofw_mapper = clknode_default_ofw_map;
#endif
SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
OID_AUTO, "clocks",
CTLTYPE_STRING | CTLFLAG_RD,
clkdom, 0, clkdom_sysctl,
"A",
"Clock list for the domain");
return (clkdom);
}
@ -503,6 +523,7 @@ clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
const struct clknode_init_def *def)
{
struct clknode *clknode;
struct sysctl_oid *clknode_oid;
KASSERT(def->name != NULL, ("clock name is NULL"));
KASSERT(def->name[0] != '\0', ("clock name is empty"));
@ -547,6 +568,42 @@ clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
clknode->parent_idx = CLKNODE_IDX_NONE;
TAILQ_INIT(&clknode->children);
sysctl_ctx_init(&clknode->sysctl_ctx);
clknode_oid = SYSCTL_ADD_NODE(&clknode->sysctl_ctx,
SYSCTL_STATIC_CHILDREN(_clock),
OID_AUTO, clknode->name,
CTLFLAG_RD, 0, "A clock node");
SYSCTL_ADD_U64(&clknode->sysctl_ctx,
SYSCTL_CHILDREN(clknode_oid),
OID_AUTO, "frequency",
CTLFLAG_RD, &clknode->freq, 0, "The clock frequency");
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
SYSCTL_CHILDREN(clknode_oid),
OID_AUTO, "parent",
CTLTYPE_STRING | CTLFLAG_RD,
clknode, CLKNODE_SYSCTL_PARENT, clknode_sysctl,
"A",
"The clock parent");
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
SYSCTL_CHILDREN(clknode_oid),
OID_AUTO, "parents",
CTLTYPE_STRING | CTLFLAG_RD,
clknode, CLKNODE_SYSCTL_PARENTS_LIST, clknode_sysctl,
"A",
"The clock parents list");
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
SYSCTL_CHILDREN(clknode_oid),
OID_AUTO, "childrens",
CTLTYPE_STRING | CTLFLAG_RD,
clknode, CLKNODE_SYSCTL_CHILDREN_LIST, clknode_sysctl,
"A",
"The clock childrens list");
SYSCTL_ADD_INT(&clknode->sysctl_ctx,
SYSCTL_CHILDREN(clknode_oid),
OID_AUTO, "enable_cnt",
CTLFLAG_RD, &clknode->enable_cnt, 0, "The clock enable counter");
return (clknode);
}
@ -1385,3 +1442,64 @@ clk_parse_ofw_clk_name(device_t dev, phandle_t node, const char **name)
return (0);
}
#endif
static int
clkdom_sysctl(SYSCTL_HANDLER_ARGS)
{
struct clkdom *clkdom = arg1;
struct clknode *clknode;
struct sbuf *sb;
int ret;
sb = sbuf_new_for_sysctl(NULL, NULL, 4096, req);
if (sb == NULL)
return (ENOMEM);
CLK_TOPO_SLOCK();
TAILQ_FOREACH(clknode, &clkdom->clknode_list, clkdom_link) {
sbuf_printf(sb, "%s ", clknode->name);
}
CLK_TOPO_UNLOCK();
ret = sbuf_finish(sb);
sbuf_delete(sb);
return (ret);
}
static int
clknode_sysctl(SYSCTL_HANDLER_ARGS)
{
struct clknode *clknode, *children;
enum clknode_sysctl_type type = arg2;
struct sbuf *sb;
const char **parent_names;
int ret, i;
clknode = arg1;
sb = sbuf_new_for_sysctl(NULL, NULL, 512, req);
if (sb == NULL)
return (ENOMEM);
CLK_TOPO_SLOCK();
switch (type) {
case CLKNODE_SYSCTL_PARENT:
if (clknode->parent)
sbuf_printf(sb, "%s", clknode->parent->name);
break;
case CLKNODE_SYSCTL_PARENTS_LIST:
parent_names = clknode_get_parent_names(clknode);
for (i = 0; i < clknode->parent_cnt; i++)
sbuf_printf(sb, "%s ", parent_names[i]);
break;
case CLKNODE_SYSCTL_CHILDREN_LIST:
TAILQ_FOREACH(children, &(clknode->children), sibling_link) {
sbuf_printf(sb, "%s ", children->name);
}
break;
}
CLK_TOPO_UNLOCK();
ret = sbuf_finish(sb);
sbuf_delete(sb);
return (ret);
}

View File

@ -88,6 +88,11 @@ SYSCTL_ROOT_NODE(OID_AUTO, regression, CTLFLAG_RW, 0,
"Regression test MIB");
#endif
#ifdef EXT_RESOURCES
SYSCTL_ROOT_NODE(OID_AUTO, clock, CTLFLAG_RW, 0,
"Clocks");
#endif
SYSCTL_STRING(_kern, OID_AUTO, ident, CTLFLAG_RD|CTLFLAG_MPSAFE,
kern_ident, 0, "Kernel identifier");

View File

@ -1004,6 +1004,9 @@ SYSCTL_DECL(_compat);
SYSCTL_DECL(_regression);
SYSCTL_DECL(_security);
SYSCTL_DECL(_security_bsd);
#ifdef EXT_RESOURCES
SYSCTL_DECL(_clock);
#endif
extern char machine[];
extern char osrelease[];