git: 1a74d77f8512 - main - extres/clk: Add a method to detect the HW state of the clock gate.

From: Michal Meloun <mmel_at_FreeBSD.org>
Date: Fri, 24 Dec 2021 18:44:25 UTC
The branch main has been updated by mmel:

URL: https://cgit.FreeBSD.org/src/commit/?id=1a74d77f851212f8cc80e6b15e30c2b252b84d48

commit 1a74d77f851212f8cc80e6b15e30c2b252b84d48
Author:     Michal Meloun <mmel@FreeBSD.org>
AuthorDate: 2021-12-24 11:18:49 +0000
Commit:     Michal Meloun <mmel@FreeBSD.org>
CommitDate: 2021-12-24 18:42:44 +0000

    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
---
 sys/dev/extres/clk/clk.c        | 31 +++++++++++++++++++++++++++++++
 sys/dev/extres/clk/clk_gate.c   | 35 +++++++++++++++++++++--------------
 sys/dev/extres/clk/clknode_if.m | 11 +++++++++++
 3 files changed, 63 insertions(+), 14 deletions(-)

diff --git a/sys/dev/extres/clk/clk.c b/sys/dev/extres/clk/clk.c
index 839f0f842a78..f4284fcd59ba 100644
--- a/sys/dev/extres/clk/clk.c
+++ b/sys/dev/extres/clk/clk.c
@@ -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();
 
diff --git a/sys/dev/extres/clk/clk_gate.c b/sys/dev/extres/clk/clk_gate.c
index e0673fd81a7e..53b2242a6f6a 100644
--- a/sys/dev/extres/clk/clk_gate.c
+++ b/sys/dev/extres/clk/clk_gate.c
@@ -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)
 {
diff --git a/sys/dev/extres/clk/clknode_if.m b/sys/dev/extres/clk/clknode_if.m
index 80d67547b695..367bc0c432ed 100644
--- a/sys/dev/extres/clk/clknode_if.m
+++ b/sys/dev/extres/clk/clknode_if.m
@@ -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
 #