feat(nxp-clk): add FXOSC clock enablement

Add the low-level implementation to enable the FXOSC oscillator, which
is disabled by default when booting the SoC. It will be used by PLLs,
for which support will be added later.

Change-Id: Ie784e4e29b8b4453b39d37594c311af940bebf92
Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
diff --git a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c
index e6653bd..c331a82 100644
--- a/drivers/nxp/clk/s32cc/s32cc_clk_drv.c
+++ b/drivers/nxp/clk/s32cc/s32cc_clk_drv.c
@@ -5,13 +5,20 @@
  */
 #include <errno.h>
 
+#include <s32cc-clk-regs.h>
+
 #include <common/debug.h>
 #include <drivers/clk.h>
+#include <lib/mmio.h>
 #include <s32cc-clk-modules.h>
 #include <s32cc-clk-utils.h>
 
 #define MAX_STACK_DEPTH		(15U)
 
+struct s32cc_clk_drv {
+	uintptr_t fxosc_base;
+};
+
 static int update_stack_depth(unsigned int *depth)
 {
 	if (*depth == 0U) {
@@ -22,9 +29,138 @@
 	return 0;
 }
 
+static struct s32cc_clk_drv *get_drv(void)
+{
+	static struct s32cc_clk_drv driver = {
+		.fxosc_base = FXOSC_BASE_ADDR,
+	};
+
+	return &driver;
+}
+
+static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth);
+
+static int enable_clk_module(const struct s32cc_clk_obj *module,
+			     const struct s32cc_clk_drv *drv,
+			     unsigned int *depth)
+{
+	const struct s32cc_clk *clk = s32cc_obj2clk(module);
+	int ret;
+
+	ret = update_stack_depth(depth);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (clk == NULL) {
+		return -EINVAL;
+	}
+
+	if (clk->module != NULL) {
+		return enable_module(clk->module, depth);
+	}
+
+	if (clk->pclock != NULL) {
+		return enable_clk_module(&clk->pclock->desc, drv, depth);
+	}
+
+	return -EINVAL;
+}
+
+static void enable_fxosc(const struct s32cc_clk_drv *drv)
+{
+	uintptr_t fxosc_base = drv->fxosc_base;
+	uint32_t ctrl;
+
+	ctrl = mmio_read_32(FXOSC_CTRL(fxosc_base));
+	if ((ctrl & FXOSC_CTRL_OSCON) != U(0)) {
+		return;
+	}
+
+	ctrl = FXOSC_CTRL_COMP_EN;
+	ctrl &= ~FXOSC_CTRL_OSC_BYP;
+	ctrl |= FXOSC_CTRL_EOCV(0x1);
+	ctrl |= FXOSC_CTRL_GM_SEL(0x7);
+	mmio_write_32(FXOSC_CTRL(fxosc_base), ctrl);
+
+	/* Switch ON the crystal oscillator. */
+	mmio_setbits_32(FXOSC_CTRL(fxosc_base), FXOSC_CTRL_OSCON);
+
+	/* Wait until the clock is stable. */
+	while ((mmio_read_32(FXOSC_STAT(fxosc_base)) & FXOSC_STAT_OSC_STAT) == U(0)) {
+	}
+}
+
+static int enable_osc(const struct s32cc_clk_obj *module,
+		      const struct s32cc_clk_drv *drv,
+		      unsigned int *depth)
+{
+	const struct s32cc_osc *osc = s32cc_obj2osc(module);
+	int ret = 0;
+
+	ret = update_stack_depth(depth);
+	if (ret != 0) {
+		return ret;
+	}
+
+	switch (osc->source) {
+	case S32CC_FXOSC:
+		enable_fxosc(drv);
+		break;
+	/* FIRC and SIRC oscillators are enabled by default */
+	case S32CC_FIRC:
+		break;
+	case S32CC_SIRC:
+		break;
+	default:
+		ERROR("Invalid oscillator %d\n", osc->source);
+		ret = -EINVAL;
+		break;
+	};
+
+	return ret;
+}
+
+static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth)
+{
+	const struct s32cc_clk_drv *drv = get_drv();
+	int ret = 0;
+
+	ret = update_stack_depth(depth);
+	if (ret != 0) {
+		return ret;
+	}
+
+	if (drv == NULL) {
+		return -EINVAL;
+	}
+
+	switch (module->type) {
+	case s32cc_osc_t:
+		ret = enable_osc(module, drv, depth);
+		break;
+	case s32cc_clk_t:
+		ret = enable_clk_module(module, drv, depth);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
 static int s32cc_clk_enable(unsigned long id)
 {
-	return -ENOTSUP;
+	unsigned int depth = MAX_STACK_DEPTH;
+	const struct s32cc_clk *clk;
+
+	clk = s32cc_get_arch_clk(id);
+	if (clk == NULL) {
+		return -EINVAL;
+	}
+
+	return enable_module(&clk->desc, &depth);
 }
 
 static void s32cc_clk_disable(unsigned long id)