aboutsummaryrefslogtreecommitdiff
path: root/src/clock.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-11-17 23:04:11 -0700
committerJosh Rahm <joshuarahm@gmail.com>2024-11-17 23:04:11 -0700
commit22c5b3e1dc4e3cf7de3f73ebbf5b59542f207f4b (patch)
tree259efcee1a6b438988e0afa95f80821b37b16ae3 /src/clock.c
parent7d64711cf7cbdf81d5a692044161ddc69e3dc33f (diff)
downloadch573-22c5b3e1dc4e3cf7de3f73ebbf5b59542f207f4b.tar.gz
ch573-22c5b3e1dc4e3cf7de3f73ebbf5b59542f207f4b.tar.bz2
ch573-22c5b3e1dc4e3cf7de3f73ebbf5b59542f207f4b.zip
System clock is sort of working.
It appears the frequency divider does not work. I've followed the data sheet, but no matter what I set the frequency divider to it appears to not work. It's possible maybe the GPIO is using an un-divided clock, but I'm not sure. Also the 32khz clock does not work I think. It might be an issue with the board. The waveform is jagged and looks awful. But I can switch from the HSE to the PLL.
Diffstat (limited to 'src/clock.c')
-rw-r--r--src/clock.c321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/clock.c b/src/clock.c
new file mode 100644
index 0000000..8bf152f
--- /dev/null
+++ b/src/clock.c
@@ -0,0 +1,321 @@
+
+#include "clock.h"
+#include <stdio.h>
+
+#include "system.h"
+#include "ch573/clock.h"
+
+#define SAFE_MODE_CLOCK enter_safe_mode(); \
+ CH573_CLOCK__CLOCK_CFG_T_INTF
+#define CLOCK CH573_CLOCK__CLOCK_CFG_T_INTF
+#define CLOCK_CFG ch573_clock__clock_cfg
+
+/* Interface to a clock. */
+struct clock_intf {
+ int (*enable_clock)(const clock_cfg_t*);
+ int (*switch_clock)(const clock_cfg_t*);
+ int (*disable_other_clocks)(const clock_cfg_t*);
+};
+
+static inline int enable_pll_clock(const clock_cfg_t*);
+static inline int switch_to_pll(const clock_cfg_t*);
+static inline int disable_for_pll(const clock_cfg_t*);
+
+static inline int enable_lse_clock(const clock_cfg_t*);
+static inline int switch_to_lse(const clock_cfg_t*);
+static inline int disable_for_lse(const clock_cfg_t*);
+
+static inline int enable_lsi_clock(const clock_cfg_t*);
+static inline int switch_to_lsi(const clock_cfg_t*);
+static inline int disable_for_lsi(const clock_cfg_t*);
+
+static inline int enable_hse_clock(const clock_cfg_t*);
+static inline int switch_to_hse(const clock_cfg_t*);
+static inline int disable_for_hse(const clock_cfg_t*);
+
+#define pll_clock \
+ ((struct clock_intf){ \
+ .enable_clock = enable_pll_clock, \
+ .switch_clock = switch_to_pll, \
+ .disable_other_clocks = disable_for_pll, \
+ })
+
+#define lse_clock \
+ ((struct clock_intf){ \
+ .enable_clock = enable_lse_clock, \
+ .switch_clock = switch_to_lse, \
+ .disable_other_clocks = disable_for_lse, \
+ })
+
+#define lsi_clock \
+ ((struct clock_intf){ \
+ .enable_clock = enable_lsi_clock, \
+ .switch_clock = switch_to_lsi, \
+ .disable_other_clocks = disable_for_lsi, \
+ })
+
+#define hse_clock \
+ ((struct clock_intf){ \
+ .enable_clock = enable_hse_clock, \
+ .switch_clock = switch_to_hse, \
+ .disable_other_clocks = disable_for_hse, \
+ })
+
+static void fast_delay()
+{
+ for (int i = 0; i < 10; ++i) {
+ asm volatile("");
+ }
+}
+
+static inline int disable_pll(void)
+{
+ SAFE_MODE_CLOCK.hfck_pwr_ctrl.clk_pll_pon.set(CLOCK_CFG, OFF);
+ return 0;
+}
+
+static inline int disable_hse(void)
+{
+ SAFE_MODE_CLOCK.hfck_pwr_ctrl.clk_xt32m_pon.set(CLOCK_CFG, OFF);
+ return 0;
+}
+
+static inline int disable_lse(void)
+{
+ SAFE_MODE_CLOCK.ck32k_config.clk_xt32k_pon.set(CLOCK_CFG, DISABLED);
+ return 0;
+}
+
+static inline int disable_lsi(void)
+{
+ SAFE_MODE_CLOCK.ck32k_config.clk_int32k_pon.set(CLOCK_CFG, DISABLED);
+ return 0;
+}
+
+static inline int enable_pll(void)
+{
+ SAFE_MODE_CLOCK.hfck_pwr_ctrl.clk_pll_pon.set(CLOCK_CFG, ON);
+ return 0;
+}
+
+static inline int enable_hse(void)
+{
+ SAFE_MODE_CLOCK.hfck_pwr_ctrl.clk_xt32m_pon.set(CLOCK_CFG, ON);
+ return 0;
+}
+
+static inline int enable_lse(void)
+{
+ SAFE_MODE_CLOCK.ck32k_config.clk_xt32k_pon.set(CLOCK_CFG, ENABLED);
+ fast_delay();
+ SAFE_MODE_CLOCK.xt32k_tune.xt32k_c_load.set(CLOCK_CFG, 0xf);
+ fast_delay();
+ return 0;
+}
+
+static inline int enable_lsi(void)
+{
+ SAFE_MODE_CLOCK.ck32k_config.clk_int32k_pon.set(CLOCK_CFG, ENABLED);
+ return 0;
+}
+
+static inline int enable_pll_clock(const clock_cfg_t* cfg)
+{
+ printf("Turning on PLL\n");
+ int ec = hse_clock.enable_clock(cfg); // The PLL uses the HSE.
+ if (ec) {
+ return ec;
+ };
+ printf("Do enable.\n");
+ enable_pll();
+ printf("Fast delay.\n");
+ fast_delay(); // Delay for a few microseconds to wait for the PLL to stablize
+ printf("Done\n");
+ return 0;
+}
+
+static inline int enable_lse_clock(const clock_cfg_t* cfg)
+{
+ enable_lse();
+ fast_delay();
+ return 0;
+}
+
+static inline int enable_lsi_clock(const clock_cfg_t* cfg)
+{
+ enable_lsi();
+ fast_delay();
+ return 0;
+}
+
+static inline int enable_hse_clock(const clock_cfg_t* cfg)
+{
+ uint8_t divisor = cfg->hse_clock_divisor;
+ // Divisor should only be 5 bits in size with a minimum value of 2.
+ if (divisor > 0x1f || divisor == 1) {
+ return 1;
+ }
+
+ // divisor = 0 => use the default value.
+ if (divisor == 0) {
+ divisor = 5; // Default value
+ }
+
+ enable_hse();
+ printf("Setting Divisor: %d\n", divisor);
+ SAFE_MODE_CLOCK.clk_sys_cfg.clk_pll_div.set(CLOCK_CFG, divisor);
+ fast_delay();
+ return 0;
+}
+
+static inline int switch_to_pll(const clock_cfg_t* cfg)
+{
+ printf("Switch to PLL.\n");
+ SAFE_MODE_CLOCK.clk_sys_cfg.clk_sys_mod.set(CLOCK_CFG, HCLK_SRC_PLL);
+ return 0;
+}
+
+static inline int switch_to_lse(const clock_cfg_t* cfg)
+{
+ fast_delay();
+ SAFE_MODE_CLOCK.ck32k_config.clk_osc32k_xt.set(CLOCK_CFG, EXTERNAL_32K_OSC);
+ fast_delay();
+ SAFE_MODE_CLOCK.clk_sys_cfg.clk_sys_mod.set(CLOCK_CFG, HCLK_SRC_CK32K);
+ return 0;
+}
+
+static inline int switch_to_lsi(const clock_cfg_t* cfg)
+{
+ SAFE_MODE_CLOCK.ck32k_config.clk_osc32k_xt.set(CLOCK_CFG, INTERNAL_32K_OSC);
+ fast_delay();
+ SAFE_MODE_CLOCK.clk_sys_cfg.clk_sys_mod.set(CLOCK_CFG, HCLK_SRC_CK32K);
+ fast_delay();
+ return 0;
+}
+
+static inline int switch_to_hse(const clock_cfg_t* cfg)
+{
+ SAFE_MODE_CLOCK.clk_sys_cfg.clk_sys_mod.set(CLOCK_CFG, HCLK_SRC_CK32M);
+ return 0;
+}
+
+static inline int disable_for_pll(const clock_cfg_t* cfg)
+{
+ printf("Disable other clocks.\n");
+ // disable_lsi();
+ // disable_lse();
+ // Need to keep HSE on
+ return 0;
+}
+
+static inline int disable_for_lse(const clock_cfg_t* cfg)
+{
+ // disable_lsi();
+ disable_pll();
+ disable_hse();
+ return 0;
+}
+
+static inline int disable_for_lsi(const clock_cfg_t* cfg)
+{
+ // disable_lse();
+ disable_pll();
+ disable_hse();
+ return 0;
+}
+
+static inline int disable_for_hse(const clock_cfg_t* cfg)
+{
+ // disable_lse();
+ // disable_lsi();
+ disable_pll();
+ return 0;
+}
+
+int get_system_clock(clock_cfg_t* out)
+{
+ int ck32k = CLOCK.ck32k_config.clk_osc32k_xt.get(CLOCK_CFG);
+ int ckSel = CLOCK.clk_sys_cfg.clk_sys_mod.get(CLOCK_CFG);
+
+ if (ckSel == HCLK_SRC_PLL) {
+ out->sel = CLOCK_SELECTION_PLL;
+ } else if (ckSel == HCLK_SRC_CK32M) {
+ out->sel = CLOCK_SELECTION_HSE;
+ } else if (ckSel == HCLK_SRC_CK32K) {
+ if (ck32k == INTERNAL_32K_OSC) {
+ out->sel = CLOCK_SELECTION_LSI;
+ } else if (ck32k == EXTERNAL_32K_OSC) {
+ out->sel = CLOCK_SELECTION_LSE;
+ } else {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+
+ out->hse_clock_divisor = 0;
+ if (out->sel == CLOCK_SELECTION_HSE || out->sel == CLOCK_SELECTION_PLL) {
+ out->hse_clock_divisor = CLOCK.clk_sys_cfg.clk_pll_div.get(CLOCK_CFG);
+ }
+
+ return 0;
+}
+
+uint32_t get_clock_freq(const clock_cfg_t* cfg)
+{
+ uint32_t freq = 0;
+
+ switch (cfg->sel) {
+ case CLOCK_SELECTION_PLL:
+ freq = 480000000 / cfg->pll_clock_divisor;
+ break;
+ case CLOCK_SELECTION_HSE:
+ freq = 32000000 / cfg->hse_clock_divisor;
+ break;
+ case CLOCK_SELECTION_LSE:
+ case CLOCK_SELECTION_LSI:
+ freq = 32000;
+ break;
+ }
+
+ return freq;
+}
+
+extern clock_chg_evt_listener_t CLOCK_CHANGE_LISTENERS_START;
+extern clock_chg_evt_listener_t CLOCK_CHANGE_LISTENERS_END;
+
+static void fire_clock_change_listeners(const clock_cfg_t* clk_cfg)
+{
+ clock_chg_evt_listener_t* cur = &CLOCK_CHANGE_LISTENERS_START;
+ while (cur < &CLOCK_CHANGE_LISTENERS_END) {
+ cur->on_clk_change(clk_cfg);
+ ++ cur;
+ }
+}
+
+int set_system_clock(const clock_cfg_t* clk_cfg)
+{
+#define clocks \
+ ((struct clock_intf[]){pll_clock, lse_clock, lsi_clock, hse_clock})
+ int ec = clocks[clk_cfg->sel].enable_clock(clk_cfg);
+
+ if (ec) {
+ return ec;
+ }
+
+ ec = clocks[clk_cfg->sel].switch_clock(clk_cfg);
+
+ if (ec) {
+ return ec;
+ }
+
+ ec = clocks[clk_cfg->sel].disable_other_clocks(clk_cfg);
+
+ if (ec) {
+ return ec;
+ }
+
+ fire_clock_change_listeners(clk_cfg);
+#undef clocks
+ return 0;
+}