extres/clk: Add a method to detect the HW state of the clock gate.

- add method to read gate enable/disable staust from HW
- show gate status in sysctl clock dump

MFC after:	1 week
This commit is contained in:
Michal Meloun 2021-12-24 12:18:49 +01:00
parent 72a2f3b5e2
commit 1a74d77f85
3 changed files with 63 additions and 14 deletions

View File

@ -186,6 +186,7 @@ enum clknode_sysctl_type {
CLKNODE_SYSCTL_PARENTS_LIST,
CLKNODE_SYSCTL_CHILDREN_LIST,
CLKNODE_SYSCTL_FREQUENCY,
CLKNODE_SYSCTL_GATE,
};
static int clknode_sysctl(SYSCTL_HANDLER_ARGS);
@ -531,6 +532,8 @@ clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
struct clknode *clknode;
struct sysctl_oid *clknode_oid;
bool replaced;
kobjop_desc_t kobj_desc;
kobj_method_t *kobj_method;
KASSERT(def->name != NULL, ("clock name is NULL"));
KASSERT(def->name[0] != '\0', ("clock name is empty"));
@ -640,6 +643,22 @@ clknode_create(struct clkdom * clkdom, clknode_class_t clknode_class,
clknode, CLKNODE_SYSCTL_FREQUENCY, clknode_sysctl,
"A",
"The clock frequency");
/* Install gate handler only if clknode have 'set_gate' method */
kobj_desc = &clknode_set_gate_desc;
kobj_method = kobj_lookup_method(((kobj_t)clknode)->ops->cls, NULL,
kobj_desc);
if (kobj_method != &kobj_desc->deflt &&
kobj_method->func != (kobjop_t)clknode_method_set_gate) {
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
SYSCTL_CHILDREN(clknode_oid),
OID_AUTO, "gate",
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
clknode, CLKNODE_SYSCTL_GATE, clknode_sysctl,
"A",
"The clock gate status");
}
SYSCTL_ADD_PROC(&clknode->sysctl_ctx,
SYSCTL_CHILDREN(clknode_oid),
OID_AUTO, "parent",
@ -1617,6 +1636,7 @@ clknode_sysctl(SYSCTL_HANDLER_ARGS)
struct sbuf *sb;
const char **parent_names;
uint64_t freq;
bool enable;
int ret, i;
clknode = arg1;
@ -1647,6 +1667,17 @@ clknode_sysctl(SYSCTL_HANDLER_ARGS)
else
sbuf_printf(sb, "Error: %d ", ret);
break;
case CLKNODE_SYSCTL_GATE:
ret = CLKNODE_GET_GATE(clknode, &enable);
if (ret == 0)
sbuf_printf(sb, enable ? "enabled": "disabled");
else if (ret == ENXIO)
sbuf_printf(sb, "unimplemented");
else if (ret == ENOENT)
sbuf_printf(sb, "unreadable");
else
sbuf_printf(sb, "Error: %d ", ret);
break;
}
CLK_TOPO_UNLOCK();

View File

@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
static int clknode_gate_init(struct clknode *clk, device_t dev);
static int clknode_gate_set_gate(struct clknode *clk, bool enable);
static int clknode_gate_get_gate(struct clknode *clk, bool *enable);
struct clknode_gate_sc {
uint32_t offset;
uint32_t shift;
@ -60,13 +61,13 @@ struct clknode_gate_sc {
uint32_t on_value;
uint32_t off_value;
int gate_flags;
bool ungated;
};
static clknode_method_t clknode_gate_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, clknode_gate_init),
CLKNODEMETHOD(clknode_set_gate, clknode_gate_set_gate),
CLKNODEMETHOD(clknode_get_gate, clknode_gate_get_gate),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(clknode_gate, clknode_gate_class, clknode_gate_methods,
@ -75,18 +76,7 @@ DEFINE_CLASS_1(clknode_gate, clknode_gate_class, clknode_gate_methods,
static int
clknode_gate_init(struct clknode *clk, device_t dev)
{
uint32_t reg;
struct clknode_gate_sc *sc;
int rv;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
rv = RD4(clk, sc->offset, &reg);
DEVICE_UNLOCK(clk);
if (rv != 0)
return (rv);
reg = (reg >> sc->shift) & sc->mask;
sc->ungated = reg == sc->on_value ? 1 : 0;
clknode_init_parent_idx(clk, 0);
return(0);
}
@ -99,10 +89,9 @@ clknode_gate_set_gate(struct clknode *clk, bool enable)
int rv;
sc = clknode_get_softc(clk);
sc->ungated = enable;
DEVICE_LOCK(clk);
rv = MD4(clk, sc->offset, sc->mask << sc->shift,
(sc->ungated ? sc->on_value : sc->off_value) << sc->shift);
(enable ? sc->on_value : sc->off_value) << sc->shift);
if (rv != 0) {
DEVICE_UNLOCK(clk);
return (rv);
@ -112,6 +101,24 @@ clknode_gate_set_gate(struct clknode *clk, bool enable)
return(0);
}
static int
clknode_gate_get_gate(struct clknode *clk, bool *enabled)
{
uint32_t reg;
struct clknode_gate_sc *sc;
int rv;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
rv = RD4(clk, sc->offset, &reg);
DEVICE_UNLOCK(clk);
if (rv != 0)
return (rv);
reg = (reg >> sc->shift) & sc->mask;
*enabled = reg == sc->on_value;
return(0);
}
int
clknode_gate_register(struct clkdom *clkdom, struct clk_gate_def *clkdef)
{

View File

@ -70,6 +70,17 @@ METHOD int set_gate {
bool enable;
};
#
# Get gate status
# Return: ENXIO - method is not implemented
# ENOENT - HW doesn't support reading of gate enable
# 0 - success
#
METHOD int get_gate {
struct clknode *clk;
bool *enabled;
};
#
# Set multiplexer
#