#ifndef SAM_CLK_H_
#define SAM_CLK_H_

#if SAM_OSC8M_ENABLED == TRUE
#define SAM_OSC8M_FREQ  SAM_OSC8_VAL
#else
#define SAM_OSC8M_FREQ 0
#endif

#if SAM_OSC32K_ENABLED == TRUE
#define SAM_OSC32K_FREQ  SAM_OSC32K_VAL
#else
#define SAM_OSC32K_FREQ 0
#endif

#if SAM_XOSC_ENABLED == TRUE
#if SAM_XOSC_FREQ > SAM_XOSC_MAX_VAL
#error "XOSC on SAMD21 can only sustain 32M"
#endif
#else
#undef SAM_XOSC_FREQ
#define SAM_XOSC_FREQ 0
#endif

#if SAM_XOSC32_ENABLED == FALSE
#undef SAM_XOSC32_FREQ
#define SAM_XOSC32_FREQ 0
#endif

#if SAM_DFLL48_ENABLED == TRUE
#define SAM_DFLL48_FREQ SAM_DFLL48_VAL
#else
#define SAM_DFLL48_FREQ 0
#endif
// GCLK0
#if SAM_GCLK0_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK0_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK0_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK0_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK0_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK0_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK0_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK0_SRC_FREQ SAM_XOSC32K_FREQ
#endif
#define SAM_GCLK0_GENDIV SAM_GCLK0_SRC_FREQ/SAM_GCLK0_FREQ
#if SAM_GCLK0_GENDIV == 0
#error "GENDIV GCLK0 == 0"
#endif

// GCLK1
#if SAM_GCLK1_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK1_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK1_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK1_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK1_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK1_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK1_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK1_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#if SAM_DFLL48_ENABLED == 1 && SAM_GCLK1_ENABLE == FALSE
#warning "DFLL48 can only use GCLK1 in this HAL, Forcing GCLK1 to enable"
#undef SAM_GCLK1_ENABLE
#define SAM_GCLK1_ENABLE TRUE
#endif
#define SAM_GCLK1_GENDIV SAM_GCLK1_SRC_FREQ/SAM_GCLK1_FREQ
#if SAM_GCLK1_GENDIV == 0 && SAM_GCLK1_ENABLE == TRUE
#error "GENDIV GCLK1 == 0"
#endif

// GCLK2
#if SAM_GCLK2_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK2_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK2_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK2_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK2_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK2_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK2_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK2_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#define SAM_GCLK2_GENDIV SAM_GCLK2_SRC_FREQ/SAM_GCLK2_FREQ
#if SAM_GCLK2_GENDIV == 0 && SAM_GCLK2_ENABLE == TRUE
#error "GENDIV GCLK2 == 0"
#endif

// GCLK3
#if SAM_GCLK3_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK3_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK3_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK3_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK3_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK3_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK3_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK3_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#define SAM_GCLK3_GENDIV SAM_GCLK3_SRC_FREQ/SAM_GCLK3_FREQ
#if SAM_GCLK3_GENDIV == 0 && SAM_GCLK3_ENABLE == TRUE
#error "GENDIV GCLK3 == 0"
#endif

// GCLK4
#if SAM_GCLK4_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK4_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK4_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK4_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK4_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK4_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK4_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK4_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#define SAM_GCLK4_GENDIV SAM_GCLK4_SRC_FREQ/SAM_GCLK4_FREQ
#if SAM_GCLK4_GENDIV == 0 && SAM_GCLK4_ENABLE == TRUE
#error "GENDIV GCLK4 == 0"
#endif

// GCLK5
#if SAM_GCLK5_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK5_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK5_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK5_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK5_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK5_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK5_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK5_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#define SAM_GCLK5_GENDIV SAM_GCLK5_SRC_FREQ/SAM_GCLK5_FREQ
#if SAM_GCLK5_GENDIV == 0 && SAM_GCLK5_ENABLE == TRUE
#error "GENDIV GCLK5 == 0"
#endif

// GCLK6
#if SAM_GCLK6_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK6_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK6_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK6_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK6_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK6_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK6_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK6_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#define SAM_GCLK6_GENDIV SAM_GCLK6_SRC_FREQ/SAM_GCLK6_FREQ
#if SAM_GCLK6_GENDIV == 0 && SAM_GCLK6_ENABLE == TRUE
#error "GENDIV GCLK6 == 0"
#endif

// GCLK7
#if SAM_GCLK7_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK7_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK7_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK7_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK7_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK7_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK7_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK7_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#define SAM_GCLK7_GENDIV SAM_GCLK7_SRC_FREQ/SAM_GCLK7_FREQ
#if SAM_GCLK7_GENDIV == 0 && SAM_GCLK7_ENABLE == TRUE
#error "GENDIV GCLK7 == 0"
#endif

// GCLK8
#if SAM_GCLK8_SRC == GCLK_GENCTRL_SRC_DFLL48M
#define SAM_GCLK8_SRC_FREQ SAM_DFLL48_FREQ
#elif SAM_GCLK8_SRC == GCLK_GENCTRL_SRC_OSC8M
#define SAM_GCLK8_SRC_FREQ SAM_OSC8_FREQ
#elif SAM_GCLK8_SRC == GCLK_GENCTRL_SRC_OSC32K
#define SAM_GCLK8_SRC_FREQ SAM_OSC32K_FREQ
#elif SAM_GCLK8_SRC == GCLK_GENCTRL_SRC_XOSC32K
#define SAM_GCLK8_SRC_FREQ SAM_XOSC32K_FREQ
#endif

#define SAM_GCLK8_GENDIV SAM_GCLK8_SRC_FREQ/SAM_GCLK8_FREQ
#if SAM_GCLK8_GENDIV == 0 && SAM_GCLK8_ENABLE == TRUE
#error "GENDIV GCLK8 == 0"
#endif

#define SAM_CPU_FREQ SAM_GCLK0_FREQ / (1UL << SAM_CPUDIV)
#define SAM_APBA_FREQ SAM_GCLK0_FREQ / (1UL << SAM_APBADIV)
#define SAM_APBB_FREQ SAM_GCLK0_FREQ / (1UL << SAM_APBADIV)
#define SAM_APBC_FREQ SAM_GCLK0_FREQ / (1UL << SAM_APBADIV)

#if SAM_CPU_FREQ > 1000000UL
#define SAM_NVM_DELAY 3
#else 
#define SAM_NVM_DELAY 0
#endif

#ifdef __cplusplus
extern "C" {
#endif
    static inline uint32_t get_cpu_freq(void) {
        return SAM_CPU_FREQ;
    }
    void sam_gclk_init(uint32_t id, 
                    uint32_t sysctrl_src, 
                    uint16_t gendiv, 
                    bool enable);
    void sam_gclk_mux(uint32_t id, 
                      uint32_t dest, 
                      bool enable);
#ifdef __cplusplus
}
#endif


#endif