/* * Copyright (c) 2021 Microchip Technology Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT microchip_xec_pcr #include #include #include #include #include #include #include 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);