[topgen] Generate Clock Description for SW

This generates indexes that can be used by `dif_clkmgr` to index into
the memory-mapped registers, in order to control specific, named,
clocks. Not all clocks are named, nor are they all software-controlled,
but the logic here matches the logic that generates the clock manger
description.

Signed-off-by: Sam Elliott <selliott@lowrisc.org>
diff --git a/hw/top_earlgrey/data/top_earlgrey.h.tpl b/hw/top_earlgrey/data/top_earlgrey.h.tpl
index 07a90ef..56caa5e 100644
--- a/hw/top_earlgrey/data/top_earlgrey.h.tpl
+++ b/hw/top_earlgrey/data/top_earlgrey.h.tpl
@@ -159,6 +159,20 @@
  */
 ${helper.pwrmgr_reset_requests.render()}
 
+/**
+ * Clock Manager Software-Controlled ("Gated") Clocks.
+ *
+ * The Software has full control over these clocks.
+ */
+${helper.clkmgr_gateable_clocks.render()}
+
+/**
+ * Clock Manager Software-Hinted Clocks.
+ *
+ * The Software has partial control over these clocks. It can ask them to stop,
+ * but the clock manager is in control of whether the clock actually is stopped.
+ */
+${helper.clkmgr_hintable_clocks.render()}
 
 // Header Extern Guard
 #ifdef __cplusplus
diff --git a/hw/top_earlgrey/sw/autogen/top_earlgrey.h b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
index 533d463..57f56ca 100644
--- a/hw/top_earlgrey/sw/autogen/top_earlgrey.h
+++ b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
@@ -831,6 +831,30 @@
   kTopEarlgreyPowerManagerResetRequestsLast = 0, /**< \internal Last valid pwrmgr reset_request signal */
 } top_earlgrey_power_manager_reset_requests_t;
 
+/**
+ * Clock Manager Software-Controlled ("Gated") Clocks.
+ *
+ * The Software has full control over these clocks.
+ */
+typedef enum top_earlgrey_gateable_clocks {
+  kTopEarlgreyGateableClocksIoDiv4Peri = 0, /**< Clock clk_io_div4_peri in group peri */
+  kTopEarlgreyGateableClocksUsbPeri = 1, /**< Clock clk_usb_peri in group peri */
+  kTopEarlgreyGateableClocksLast = 1, /**< \internal Last Valid Gateable Clock */
+} top_earlgrey_gateable_clocks_t;
+
+/**
+ * Clock Manager Software-Hinted Clocks.
+ *
+ * The Software has partial control over these clocks. It can ask them to stop,
+ * but the clock manager is in control of whether the clock actually is stopped.
+ */
+typedef enum top_earlgrey_hintable_clocks {
+  kTopEarlgreyHintableClocksMainAes = 0, /**< Clock clk_main_aes in group trans */
+  kTopEarlgreyHintableClocksMainHmac = 1, /**< Clock clk_main_hmac in group trans */
+  kTopEarlgreyHintableClocksMainKmac = 2, /**< Clock clk_main_kmac in group trans */
+  kTopEarlgreyHintableClocksMainOtbn = 3, /**< Clock clk_main_otbn in group trans */
+  kTopEarlgreyHintableClocksLast = 3, /**< \internal Last Valid Hintable Clock */
+} top_earlgrey_hintable_clocks_t;
 
 // Header Extern Guard
 #ifdef __cplusplus
diff --git a/util/topgen/c.py b/util/topgen/c.py
index 070d218..d062825 100644
--- a/util/topgen/c.py
+++ b/util/topgen/c.py
@@ -48,6 +48,9 @@
     def as_c_type(self):
         return self.as_snake_case() + "_t"
 
+    def remove_part(self, part_to_remove):
+        return Name([p for p in self.parts if p != part_to_remove])
+
 
 class MemoryRegion(object):
     def __init__(self, name, base_addr, size_bytes):
@@ -150,6 +153,7 @@
         self._init_pwrmgr_wakeups()
         self._init_rstmgr_sw_rsts()
         self._init_pwrmgr_reset_requests()
+        self._init_clkmgr_clocks()
 
     def modules(self):
         return [(m["name"],
@@ -404,3 +408,43 @@
         enum.add_last_constant("Last valid pwrmgr reset_request signal")
 
         self.pwrmgr_reset_requests = enum
+
+    def _init_clkmgr_clocks(self):
+        """
+        Creates CEnums for accessing the software-controlled clocks in the
+        design.
+
+        The logic here matches the logic in topgen.py in how it instantiates the
+        clock manager with the described clocks.
+
+        We differentiate "gateable" clocks and "hintable" clocks because the
+        clock manager has separate register interfaces for each group.
+        """
+
+
+        aon_clocks = set()
+        for src in self.top['clocks']['srcs'] + self.top['clocks']['derived_srcs']:
+            if src['aon'] == 'yes':
+                aon_clocks.add(src['name'])
+
+        gateable_clocks = CEnum(self._top_name + Name(["gateable", "clocks"]))
+        hintable_clocks = CEnum(self._top_name + Name(["hintable", "clocks"]))
+
+        # This replicates the behaviour in `topgen.py` in deriving `hints` and
+        # `sw_clocks`.
+        for group in self.top['clocks']['groups']:
+            for (name, source) in group['clocks'].items():
+                if source not in aon_clocks:
+                    # All these clocks start with `clk_` which is redundant.
+                    clock_name = Name.from_snake_case(name).remove_part("clk")
+                    docstring = "Clock {} in group {}".format(name, group['name'])
+                    if group["sw_cg"] == "yes":
+                        gateable_clocks.add_constant(clock_name, docstring)
+                    elif group["sw_cg"] == "hint":
+                        hintable_clocks.add_constant(clock_name, docstring)
+
+        gateable_clocks.add_last_constant("Last Valid Gateable Clock")
+        hintable_clocks.add_last_constant("Last Valid Hintable Clock")
+
+        self.clkmgr_gateable_clocks = gateable_clocks
+        self.clkmgr_hintable_clocks = hintable_clocks