| /* |
| * Copyright (c) 2021 Microchip Technology Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT microchip_xec_pcr |
| |
| #include <soc.h> |
| #include <zephyr/arch/cpu.h> |
| #include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h> |
| #include <zephyr/drivers/clock_control.h> |
| #include <zephyr/drivers/clock_control/mchp_xec_clock_control.h> |
| #include <zephyr/dt-bindings/clock/mchp_xec_pcr.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(clock_control_xec, LOG_LEVEL_ERR); |
| |
| #define CLK32K_SIL_OSC_DELAY 256 |
| #define CLK32K_PLL_LOCK_WAIT (16 * 1024) |
| #define CLK32K_PIN_WAIT 4096 |
| #define CLK32K_XTAL_WAIT (16 * 1024) |
| #define CLK32K_XTAL_MON_WAIT (64 * 1024) |
| |
| /* |
| * Counter checks: |
| * 32KHz period counter minimum for pass/fail: 16-bit |
| * 32KHz period counter maximum for pass/fail: 16-bit |
| * 32KHz duty cycle variation max for pass/fail: 16-bit |
| * 32KHz valid count minimum: 8-bit |
| * |
| * 32768 Hz period is 30.518 us |
| * HW count resolution is 48 MHz. |
| * One 32KHz clock pulse = 1464.84 48 MHz counts. |
| */ |
| #define CNT32K_TMIN 1435 |
| #define CNT32K_TMAX 1495 |
| #define CNT32K_DUTY_MAX 74 |
| #define CNT32K_VAL_MIN 4 |
| |
| #define DEST_PLL 0 |
| #define DEST_PERIPH 1 |
| |
| #define CLK32K_FLAG_CRYSTAL_SE BIT(0) |
| #define CLK32K_FLAG_PIN_FB_CRYSTAL BIT(1) |
| |
| #define PCR_PERIPH_RESET_SPIN 8u |
| |
| #define HIBTIMER_10_MS 328u |
| #define HIBTIMER_300_MS 9830u |
| |
| #define PCR_XEC_REG_BASE \ |
| ((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr)))) |
| |
| #define HIBTIMER_0_XEC_REG_BASE \ |
| ((struct htmr_regs *)(DT_REG_ADDR(DT_NODELABEL(hibtimer0)))) |
| |
| #define GIRQ23_XEC_REG_BASE \ |
| ((struct girq_regs *)(DT_REG_ADDR(DT_NODELABEL(girq23)))) |
| |
| enum clk32k_src { |
| CLK32K_SRC_SIL_OSC = 0, |
| CLK32K_SRC_CRYSTAL, |
| CLK32K_SRC_MAX |
| }; |
| |
| enum clk32k_dest { CLK32K_DEST_PLL = 0, CLK32K_DEST_PERIPH, CLK32K_DEST_MAX }; |
| |
| |
| /* Driver config */ |
| struct xec_pcr_config { |
| uintptr_t pcr_base; /* pcr base address */ |
| uintptr_t vbr_base; /* vbat registers base address */ |
| }; |
| |
| /* Driver convenience defines */ |
| |
| #define PCR_NODE_LBL DT_NODELABEL(pcr) |
| |
| #define XEC_CORE_CLK_DIV \ |
| DT_PROP_OR(PCR_NODE_LBL, core_clk_div, CONFIG_SOC_MEC172X_PROC_CLK_DIV) |
| |
| #define DRV_CONFIG(dev) \ |
| ((const struct xec_pcr_config *)(dev)->config) |
| |
| #define XEC_PCR_REGS_BASE(dev) \ |
| (struct pcr_regs *)(DRV_CONFIG(dev)->pcr_base) |
| |
| #define XEC_VBATR_REGS_BASE(dev) \ |
| (struct vbatr_regs *)(DRV_CONFIG(dev)->vbr_base) |
| |
| /* |
| * In early Zephyr initialization we don't have timer services. Also, the SoC |
| * may be running on its ring oscillator (+/- 50% accuracy). Configuring the |
| * SoC's clock subsystem requires wait/delays. We implement a simple delay |
| * by writing to a read-only hardware register in the PCR block. |
| */ |
| static uint32_t spin_delay(struct pcr_regs *pcr, uint32_t cnt) |
| { |
| uint32_t n; |
| |
| for (n = 0U; n < cnt; n++) { |
| pcr->OSC_ID = n; |
| } |
| |
| return n; |
| } |
| |
| /* |
| * Make sure PCR sleep enables are clear except for crypto |
| * which do not have internal clock gating. |
| */ |
| static void pcr_slp_init(struct pcr_regs *pcr) |
| { |
| pcr->SYS_SLP_CTRL = 0U; |
| SCB->SCR &= ~BIT(2); |
| |
| for (int i = 0; i < MCHP_MAX_PCR_SCR_REGS; i++) { |
| pcr->SLP_EN[i] = 0U; |
| } |
| |
| pcr->SLP_EN[3] = MCHP_PCR3_CRYPTO_MASK; |
| } |
| |
| static bool is_sil_osc_enabled(struct vbatr_regs *vbr) |
| { |
| if (vbr->CLK32_SRC & MCHP_VBATR_CS_SO_EN) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static void enable_sil_osc(struct vbatr_regs *vbr) |
| { |
| vbr->CLK32_SRC |= MCHP_VBATR_CS_SO_EN; |
| } |
| |
| /* caller has enabled internal silicon 32 KHz oscillator */ |
| static void hib_timer_delay(uint16_t hib_timer_count) |
| { |
| struct htmr_regs *htmr0 = HIBTIMER_0_XEC_REG_BASE; |
| struct girq_regs *girq23 = GIRQ23_XEC_REG_BASE; |
| |
| htmr0->PRLD = 0; /* disable */ |
| htmr0->CTRL = 0; /* 32k time base */ |
| girq23->SRC = BIT(16); /* clear hibernation timer 0 status */ |
| htmr0->PRLD = hib_timer_count; |
| if (hib_timer_count == 0) { |
| return; |
| } |
| |
| while ((girq23->SRC & BIT(16)) == 0) { |
| ; |
| } |
| |
| girq23->SRC = BIT(16); |
| htmr0->PRLD = 0; /* disable */ |
| } |
| |
| /* |
| * Start external 32 KHz crystal. |
| * Assumes peripheral clocks source is Silicon OSC. |
| * If current configuration matches desired crystal configuration do nothing. |
| * NOTE: Crystal requires ~300 ms to stabilize. |
| */ |
| static int enable_32k_crystal(const struct device *dev, uint32_t flags) |
| { |
| struct vbatr_regs *const vbr = XEC_VBATR_REGS_BASE(dev); |
| uint32_t vbcs = vbr->CLK32_SRC; |
| uint32_t cfg = MCHP_VBATR_CS_XTAL_EN; |
| |
| if (flags & CLK32K_FLAG_CRYSTAL_SE) { |
| cfg |= MCHP_VBATR_CS_XTAL_SE; |
| } |
| |
| if ((vbcs & cfg) == cfg) { |
| return 0; |
| } |
| |
| /* Configure crystal connection before enabling the crystal. */ |
| vbr->CLK32_SRC &= ~(MCHP_VBATR_CS_XTAL_SE | MCHP_VBATR_CS_XTAL_DHC | |
| MCHP_VBATR_CS_XTAL_CNTR_MSK); |
| if (flags & CLK32K_FLAG_CRYSTAL_SE) { |
| vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_SE; |
| } |
| |
| /* Set crystal gain */ |
| vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_CNTR_DG; |
| |
| /* enable crystal */ |
| vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_EN; |
| /* wait for crystal stabilization */ |
| hib_timer_delay(HIBTIMER_300_MS); |
| /* turn off crystal high startup current */ |
| vbr->CLK32_SRC |= MCHP_VBATR_CS_XTAL_DHC; |
| |
| return 0; |
| } |
| |
| /* |
| * Use PCR clock monitor hardware to test crystal output. |
| * Requires crystal to have stabilized after enable. |
| * When enabled the clock monitor hardware measures high/low, edges, and |
| * duty cycle and compares to programmed limits. |
| */ |
| static int check_32k_crystal(const struct device *dev) |
| { |
| struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev); |
| struct htmr_regs *htmr0 = HIBTIMER_0_XEC_REG_BASE; |
| struct girq_regs *girq23 = GIRQ23_XEC_REG_BASE; |
| uint32_t status = 0; |
| int rc = 0; |
| |
| htmr0->PRLD = 0; |
| htmr0->CTRL = 0; |
| girq23->SRC = BIT(16); |
| |
| pcr->CNT32K_CTRL = 0U; |
| pcr->CLK32K_MON_IEN = 0U; |
| pcr->CLK32K_MON_ISTS = MCHP_PCR_CLK32M_ISTS_MASK; |
| |
| pcr->CNT32K_PER_MIN = CNT32K_TMIN; |
| pcr->CNT32K_PER_MAX = CNT32K_TMAX; |
| pcr->CNT32K_DV_MAX = CNT32K_DUTY_MAX; |
| pcr->CNT32K_VALID_MIN = CNT32K_VAL_MIN; |
| |
| pcr->CNT32K_CTRL = |
| MCHP_PCR_CLK32M_CTRL_PER_EN | MCHP_PCR_CLK32M_CTRL_DC_EN | |
| MCHP_PCR_CLK32M_CTRL_VAL_EN | MCHP_PCR_CLK32M_CTRL_CLR_CNT; |
| |
| rc = -ETIMEDOUT; |
| htmr0->PRLD = HIBTIMER_10_MS; |
| status = pcr->CLK32K_MON_ISTS; |
| |
| while ((girq23->SRC & BIT(16)) == 0) { |
| if (status == (MCHP_PCR_CLK32M_ISTS_PULSE_RDY | |
| MCHP_PCR_CLK32M_ISTS_PASS_PER | |
| MCHP_PCR_CLK32M_ISTS_PASS_DC | |
| MCHP_PCR_CLK32M_ISTS_VALID)) { |
| rc = 0; |
| break; |
| } |
| |
| if (status & (MCHP_PCR_CLK32M_ISTS_FAIL | |
| MCHP_PCR_CLK32M_ISTS_STALL)) { |
| rc = -EBUSY; |
| break; |
| } |
| |
| status = pcr->CLK32K_MON_ISTS; |
| } |
| |
| pcr->CNT32K_CTRL = 0u; |
| htmr0->PRLD = 0; |
| girq23->SRC = BIT(16); |
| |
| return rc; |
| } |
| |
| /* |
| * Set the clock source for either PLL or Peripheral-32K clock domain. |
| * The source must be a stable 32 KHz input: internal silicon oscillator, |
| * external crystal (parallel or single ended connection), or a 50% duty cycle |
| * waveform on the 32KHZ_PIN. The driver does not implement 32KHZ_PIN support |
| * at this time. |
| */ |
| static void connect_32k_source(const struct device *dev, enum clk32k_src src, |
| enum clk32k_dest dest, uint32_t flags) |
| { |
| struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev); |
| struct vbatr_regs *const vbr = XEC_VBATR_REGS_BASE(dev); |
| |
| if (dest == CLK32K_DEST_PLL) { |
| switch (src) { |
| case CLK32K_SRC_SIL_OSC: |
| pcr->CLK32K_SRC_VTR = MCHP_PCR_VTR_32K_SRC_SILOSC; |
| break; |
| case CLK32K_SRC_CRYSTAL: |
| pcr->CLK32K_SRC_VTR = MCHP_PCR_VTR_32K_SRC_XTAL; |
| break; |
| default: /* do not touch HW */ |
| break; |
| } |
| } else if (dest == CLK32K_DEST_PERIPH) { |
| uint32_t vbcs = vbr->CLK32_SRC & ~(MCHP_VBATR_CS_PCS_MSK); |
| |
| switch (src) { |
| case CLK32K_SRC_SIL_OSC: |
| vbr->CLK32_SRC = vbcs | MCHP_VBATR_CS_PCS_VTR_VBAT_SO; |
| break; |
| case CLK32K_SRC_CRYSTAL: |
| vbr->CLK32_SRC = vbcs | MCHP_VBATR_CS_PCS_VTR_VBAT_XTAL; |
| break; |
| default: /* do not touch HW */ |
| break; |
| } |
| } |
| } |
| |
| /* |
| * This routine checks if the PLL is locked to its input source. Minimum lock |
| * time is 3.3 ms. Lock time can be larger when the source is an external |
| * crystal. Crystal cold start times may vary greatly based on many factors. |
| * Crystals do not like being power cycled. |
| */ |
| static int pll_wait_lock(struct pcr_regs *const pcr, uint32_t wait_cnt) |
| { |
| while (!(pcr->OSC_ID & MCHP_PCR_OSC_ID_PLL_LOCK)) { |
| if (wait_cnt == 0) { |
| return -ETIMEDOUT; |
| } |
| --wait_cnt; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * MEC172x has two 32 KHz clock domains |
| * PLL domain: 32 KHz clock input for PLL to produce 96 MHz and 48 MHz clocks |
| * Peripheral domain: 32 KHz clock for subset of peripherals. |
| * Each domain 32 KHz clock input can be from one of the following sources: |
| * Internal Silicon oscillator: +/- 2% |
| * External Crystal connected as parallel or single ended |
| * External 32KHZ_PIN 50% duty cycle waveform with fall back to either |
| * Silicon OSC or crystal when 32KHZ_PIN signal goes away or VTR power rail |
| * goes off. |
| * At chip reset the PLL is held in reset and the +/- 50% ring oscillator is |
| * the main clock. |
| * If no VBAT reset occurs the VBAT 32 KHz source register maintains its state. |
| */ |
| static int soc_clk32_init(const struct device *dev, |
| enum clk32k_src pll_clk_src, |
| enum clk32k_src periph_clk_src, |
| uint32_t flags) |
| { |
| struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev); |
| struct vbatr_regs *const vbr = XEC_VBATR_REGS_BASE(dev); |
| int rc = 0; |
| |
| /* disable PCR 32K monitor and clear counters */ |
| pcr->CNT32K_CTRL = MCHP_PCR_CLK32M_CTRL_CLR_CNT; |
| pcr->CLK32K_MON_ISTS = MCHP_PCR_CLK32M_ISTS_MASK; |
| pcr->CLK32K_MON_IEN = 0; |
| |
| if (!is_sil_osc_enabled(vbr)) { |
| enable_sil_osc(vbr); |
| spin_delay(pcr, CLK32K_SIL_OSC_DELAY); |
| } |
| |
| /* Default to 32KHz Silicon OSC for PLL and peripherals */ |
| connect_32k_source(dev, CLK32K_SRC_SIL_OSC, CLK32K_DEST_PLL, 0); |
| connect_32k_source(dev, CLK32K_SRC_SIL_OSC, CLK32K_DEST_PERIPH, 0); |
| |
| rc = pll_wait_lock(pcr, CLK32K_PLL_LOCK_WAIT); |
| if (rc) { |
| return rc; |
| } |
| |
| /* We only allow Silicon OSC or Crystal as a source. */ |
| if ((pll_clk_src == CLK32K_SRC_CRYSTAL) || |
| (periph_clk_src == CLK32K_SRC_CRYSTAL)) { |
| enable_32k_crystal(dev, flags); |
| rc = check_32k_crystal(dev); |
| if (rc) { |
| /* disable crystal */ |
| vbr->CLK32_SRC &= ~(MCHP_VBATR_CS_XTAL_EN); |
| return rc; |
| } |
| if (pll_clk_src == CLK32K_SRC_CRYSTAL) { |
| connect_32k_source(dev, CLK32K_SRC_CRYSTAL, |
| CLK32K_DEST_PLL, flags); |
| } |
| if (periph_clk_src == CLK32K_SRC_CRYSTAL) { |
| connect_32k_source(dev, CLK32K_SRC_CRYSTAL, |
| CLK32K_DEST_PERIPH, flags); |
| } |
| rc = pll_wait_lock(pcr, CLK32K_PLL_LOCK_WAIT); |
| } |
| |
| return rc; |
| } |
| |
| /* |
| * MEC172x Errata document DS80000913C |
| * Programming the PCR clock divider that divides the clock input to the ARM |
| * Cortex-M4 may cause a clock glitch. The recommended work-around is to |
| * issue four NOP instruction before and after the write to the PCR processor |
| * clock control register. The final four NOP instructions are followed by |
| * data and instruction barriers to flush the Cortex-M4's pipeline. |
| * NOTE: Zephyr provides inline functions for Cortex-Mx NOP but not for |
| * data and instruction barrier instructions. Caller's should only invoke this |
| * function with interrupts locked. |
| */ |
| static void xec_clock_control_core_clock_divider_set(uint8_t clkdiv) |
| { |
| struct pcr_regs *const pcr = |
| (struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr))); |
| |
| arch_nop(); |
| arch_nop(); |
| arch_nop(); |
| arch_nop(); |
| pcr->PROC_CLK_CTRL = (uint32_t)clkdiv; |
| arch_nop(); |
| arch_nop(); |
| arch_nop(); |
| arch_nop(); |
| __DSB(); |
| __ISB(); |
| } |
| |
| /* |
| * PCR peripheral sleep enable allows the clocks to a specific peripheral to |
| * be gated off if the peripheral is not requesting a clock. |
| * slp_idx = zero based index into 32-bit PCR sleep enable registers. |
| * slp_pos = bit position in the register |
| * slp_en if non-zero set the bit else clear the bit |
| */ |
| int z_mchp_xec_pcr_periph_sleep(uint8_t slp_idx, uint8_t slp_pos, |
| uint8_t slp_en) |
| { |
| struct pcr_regs *regs = PCR_XEC_REG_BASE; |
| |
| if ((slp_idx >= MCHP_MAX_PCR_SCR_REGS) || (slp_pos >= 32)) { |
| return -EINVAL; |
| } |
| |
| if (slp_en) { |
| regs->SLP_EN[slp_idx] |= BIT(slp_pos); |
| } else { |
| regs->SLP_EN[slp_idx] &= ~BIT(slp_pos); |
| } |
| |
| return 0; |
| } |
| |
| /* clock control driver API implementation */ |
| |
| static int xec_cc_on(const struct device *dev, |
| clock_control_subsys_t sub_system, |
| bool turn_on) |
| { |
| struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev); |
| struct mchp_xec_pcr_clk_ctrl *cc = |
| (struct mchp_xec_pcr_clk_ctrl *)sub_system; |
| uint16_t pcr_idx = 0; |
| uint16_t bitpos = 0; |
| |
| if (!cc) { |
| return -EINVAL; |
| } |
| |
| switch (MCHP_XEC_CLK_SRC_GET(cc->pcr_info)) { |
| case MCHP_XEC_PCR_CLK_CORE: |
| case MCHP_XEC_PCR_CLK_BUS: |
| break; |
| case MCHP_XEC_PCR_CLK_CPU: |
| if (cc->pcr_info & MCHP_XEC_CLK_CPU_MASK) { |
| uint32_t lock = irq_lock(); |
| |
| xec_clock_control_core_clock_divider_set( |
| cc->pcr_info & MCHP_XEC_CLK_CPU_MASK); |
| |
| irq_unlock(lock); |
| } else { |
| return -EINVAL; |
| } |
| break; |
| case MCHP_XEC_PCR_CLK_PERIPH: |
| case MCHP_XEC_PCR_CLK_PERIPH_FAST: |
| pcr_idx = MCHP_XEC_PCR_SCR_GET_IDX(cc->pcr_info); |
| bitpos = MCHP_XEC_PCR_SCR_GET_BITPOS(cc->pcr_info); |
| |
| if (pcr_idx >= MCHP_MAX_PCR_SCR_REGS) { |
| return -EINVAL; |
| } |
| |
| if (turn_on) { |
| pcr->SLP_EN[pcr_idx] &= ~BIT(bitpos); |
| } else { |
| pcr->SLP_EN[pcr_idx] |= BIT(bitpos); |
| } |
| break; |
| case MCHP_XEC_PCR_CLK_PERIPH_SLOW: |
| if (turn_on) { |
| pcr->SLOW_CLK_CTRL = |
| cc->pcr_info & MCHP_XEC_CLK_SLOW_MASK; |
| } else { |
| pcr->SLOW_CLK_CTRL = 0; |
| } |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Turn on requested clock source. |
| * Core, CPU, and Bus clocks are always on except in deep sleep state. |
| * Peripheral clocks can be gated off if the peripheral's PCR sleep enable |
| * is set and the peripheral indicates it does not need a clock by clearing |
| * its PCR CLOCK_REQ read-only status. |
| * Peripheral slow clock my be turned on by writing a non-zero divider value |
| * to its PCR control register. |
| */ |
| static int xec_clock_control_on(const struct device *dev, |
| clock_control_subsys_t sub_system) |
| { |
| return xec_cc_on(dev, sub_system, true); |
| } |
| |
| /* |
| * Turn off clock source. |
| * Core, CPU, and Bus clocks are always on except in deep sleep when PLL is |
| * turned off. Exception is 32 KHz clock. |
| * Peripheral clocks are gated off when the peripheral's sleep enable is set |
| * and the peripheral indicates is no longer needs a clock by de-asserting |
| * its read-only PCR CLOCK_REQ bit. |
| * Peripheral slow clock can be turned off by writing 0 to its control register. |
| */ |
| static inline int xec_clock_control_off(const struct device *dev, |
| clock_control_subsys_t sub_system) |
| { |
| return xec_cc_on(dev, sub_system, false); |
| } |
| |
| /* |
| * MEC172x clock subsystem: |
| * Two main clock domains: PLL and Peripheral-32K. Each domain's 32 KHz source |
| * can be selected from one of three inputs: |
| * internal silicon OSC +/- 2% accuracy |
| * external crystal connected parallel or single ended |
| * external 32 KHz 50% duty cycle waveform on 32KHZ_IN pin. |
| * PLL domain supplies 96 MHz, 48 MHz, and other high speed clocks to all |
| * peripherals except those in the Peripheral-32K clock domain. The slow clock |
| * is derived from the 48 MHz produced by the PLL. |
| * ARM Cortex-M4 core input: 96MHz |
| * AHB clock input: 48 MHz |
| * Fast AHB peripherals: 96 MHz internal and 48 MHz AHB interface. |
| * Slow clock peripherals: PWM, TACH, PROCHOT |
| * Peripheral-32K domain peripherals: |
| * WDT, RTC, RTOS timer, hibernation timers, week timer |
| * |
| * Peripherals using both PLL and 32K clock domains: |
| * BBLED, RPMFAN |
| */ |
| static int xec_clock_control_get_subsys_rate(const struct device *dev, |
| clock_control_subsys_t sub_system, |
| uint32_t *rate) |
| { |
| struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev); |
| uint32_t bus = (uint32_t)sub_system; |
| uint32_t temp = 0; |
| |
| switch (bus) { |
| case MCHP_XEC_PCR_CLK_CORE: |
| case MCHP_XEC_PCR_CLK_PERIPH_FAST: |
| *rate = MHZ(96); |
| break; |
| case MCHP_XEC_PCR_CLK_CPU: |
| /* if PCR PROC_CLK_CTRL is 0 the chip is not running */ |
| *rate = MHZ(96) / pcr->PROC_CLK_CTRL; |
| break; |
| case MCHP_XEC_PCR_CLK_BUS: |
| case MCHP_XEC_PCR_CLK_PERIPH: |
| *rate = MHZ(48); |
| break; |
| case MCHP_XEC_PCR_CLK_PERIPH_SLOW: |
| temp = pcr->SLOW_CLK_CTRL; |
| if (pcr->SLOW_CLK_CTRL) { |
| *rate = MHZ(48) / temp; |
| } else { |
| *rate = 0; /* slow clock off */ |
| } |
| break; |
| default: |
| *rate = 0; |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| #if defined(CONFIG_PM) |
| void mchp_xec_clk_ctrl_sys_sleep_enable(bool is_deep) |
| { |
| struct pcr_regs *const pcr = |
| (struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr))); |
| uint32_t sys_sleep_mode = MCHP_PCR_SYS_SLP_CTRL_SLP_ALL; |
| |
| if (is_deep) { |
| sys_sleep_mode |= MCHP_PCR_SYS_SLP_CTRL_SLP_HEAVY; |
| } |
| |
| SCB->SCR |= BIT(2); |
| pcr->SYS_SLP_CTRL = sys_sleep_mode; |
| } |
| |
| void mchp_xec_clk_ctrl_sys_sleep_disable(void) |
| { |
| struct pcr_regs *const pcr = |
| (struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr))); |
| |
| pcr->SYS_SLP_CTRL = 0; |
| SCB->SCR &= ~BIT(2); |
| } |
| #endif |
| |
| /* Clock controller driver registration */ |
| static struct clock_control_driver_api xec_clock_control_api = { |
| .on = xec_clock_control_on, |
| .off = xec_clock_control_off, |
| .get_rate = xec_clock_control_get_subsys_rate, |
| }; |
| |
| static int xec_clock_control_init(const struct device *dev) |
| { |
| int rc = 0; |
| uint32_t clk32_flags = 0; |
| struct pcr_regs *const pcr = XEC_PCR_REGS_BASE(dev); |
| enum clk32k_src clk_src_pll = |
| DT_PROP_OR(PCR_NODE_LBL, pll_32k_src, CLK32K_SRC_SIL_OSC); |
| enum clk32k_src clk_src_periph = |
| DT_PROP_OR(PCR_NODE_LBL, periph_32k_src, CLK32K_SRC_SIL_OSC); |
| |
| pcr_slp_init(pcr); |
| |
| rc = soc_clk32_init(dev, clk_src_pll, clk_src_periph, clk32_flags); |
| __ASSERT(rc == 0, "XEC: PLL and 32 KHz clock initialization failed"); |
| |
| xec_clock_control_core_clock_divider_set(XEC_CORE_CLK_DIV); |
| |
| return rc; |
| } |
| |
| const struct xec_pcr_config xec_config = { |
| .pcr_base = DT_INST_REG_ADDR_BY_IDX(0, 0), |
| .vbr_base = DT_INST_REG_ADDR_BY_IDX(0, 1), |
| }; |
| |
| DEVICE_DT_INST_DEFINE(0, |
| &xec_clock_control_init, |
| NULL, |
| NULL, &xec_config, |
| PRE_KERNEL_1, |
| CONFIG_CLOCK_CONTROL_INIT_PRIORITY, |
| &xec_clock_control_api); |