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)