/* * Copyright (c) 2025 ITE Corporation. All Rights Reserved. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ite_it51xxx_ecpm #include #include #include #include #include #include #include LOG_MODULE_REGISTER(clock_control_it51xxx, LOG_LEVEL_ERR); #include "soc_espi.h" BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "only one ite,it51xxx-ecpm compatible node can be supported"); /* it51xxx ECPM registers definition */ /* 0x02: Clock Gating Control 2 register */ #define ECPM_CGCTRL2R 0x02 #define ECPM_CIRCG BIT(5) #define ECPM_SWUCCG BIT(4) /* 0x03: PLL Control */ #define ECPM_PLLCTRL 0x03 /* 0x04: Auto Clock Gating */ #define ECPM_AUTOCG 0x04 #define ECPM_AUART1CG BIT(6) #define ECPM_AUART2CG BIT(5) #define ECPM_ASSPICG BIT(4) #define ECPM_ACIRCG BIT(2) /* 0x02: Clock Gating Control 5 register */ #define ECPM_CGCTRL3R 0x05 #define ECPM_PECICG BIT(3) #define ECPM_SSPICG BIT(1) /* 0x06: PLL Frequency */ #define ECPM_PLLFREQR 0x06 #define ECPM_PLLFREQ_MASK GENMASK(3, 0) static const uint8_t pll_cfg[] = { [PLL_18400_KHZ] = 0x01, [PLL_32300_KHZ] = 0x03, [PLL_64500_KHZ] = 0x07, [PLL_48000_KHZ] = 0x09, }; struct clock_control_it51xxx_data { const uint8_t *pll_configuration; }; /* Driver config */ struct clock_control_it51xxx_config { mm_reg_t ecpm_base; int pll_freq; }; /* Clock controller local functions */ static inline int clock_control_it51xxx_on(const struct device *dev, clock_control_subsys_t sub_system) { const struct clock_control_it51xxx_config *const config = dev->config; struct ite_clk_cfg *clk_cfg = (struct ite_clk_cfg *)(sub_system); /* Enable the clock of this module */ sys_write8(sys_read8(config->ecpm_base + clk_cfg->ctrl) & ~(clk_cfg->bits), config->ecpm_base + clk_cfg->ctrl); return 0; } static inline int clock_control_it51xxx_off(const struct device *dev, clock_control_subsys_t sub_system) { const struct clock_control_it51xxx_config *const config = dev->config; struct ite_clk_cfg *clk_cfg = (struct ite_clk_cfg *)(sub_system); uint8_t tmp_mask = 0; /* CGCTRL3R, bit 6, must always write a 1. */ tmp_mask = (clk_cfg->ctrl == IT51XXX_ECPM_CGCTRL3R_OFF) ? 0x40 : 0x00; sys_write8(sys_read8(config->ecpm_base + clk_cfg->ctrl) | clk_cfg->bits | tmp_mask, config->ecpm_base + clk_cfg->ctrl); return 0; } static int clock_control_it51xxx_get_rate(const struct device *dev, clock_control_subsys_t sub_system, uint32_t *rate) { const struct clock_control_it51xxx_config *const config = dev->config; int reg_val = sys_read8(config->ecpm_base + ECPM_PLLFREQR) & ECPM_PLLFREQ_MASK; switch (reg_val) { case 0x01: *rate = KHZ(18400); break; case 0x03: *rate = KHZ(32300); break; case 0x07: *rate = KHZ(64500); break; case 0x09: *rate = KHZ(48000); break; default: return -ERANGE; } return 0; } static void pll_change_isr(const void *unused) { ARG_UNUSED(unused); /* * We are here because we have completed changing PLL sequence, * so disabled PLL frequency change event interrupt. */ irq_disable(IT51XXX_IRQ_PLL_CHANGE); } static void chip_configure_pll(const struct device *dev, uint8_t pll) { const struct clock_control_it51xxx_config *config = dev->config; /* Set pll frequency change event */ IRQ_CONNECT(IT51XXX_IRQ_PLL_CHANGE, 0, pll_change_isr, NULL, IRQ_TYPE_EDGE_RISING); /* Clear interrupt status of pll frequency change event */ ite_intc_isr_clear(IT51XXX_IRQ_PLL_CHANGE); irq_enable(IT51XXX_IRQ_PLL_CHANGE); /* * Configure PLL clock dividers. * Writing data to these registers doesn't change the PLL frequency immediately until the * status is changed into wakeup from the sleep mode. * The following code is intended to make the system enter sleep mode, and wait PLL * frequency change event to wakeup chip to complete PLL update. */ sys_write8(pll, config->ecpm_base + ECPM_PLLFREQR); /* Chip sleep after wait for interrupt (wfi) instruction */ chip_pll_ctrl(CHIP_PLL_SLEEP); /* Chip sleep and wait timer wake it up */ __asm__ volatile("wfi"); /* Chip sleep and wait timer wake it up */ chip_pll_ctrl(CHIP_PLL_DOZE); } static int clock_control_it51xxx_init(const struct device *dev) { const struct clock_control_it51xxx_config *config = dev->config; struct clock_control_it51xxx_data *data = dev->data; int reg_val = sys_read8(config->ecpm_base + ECPM_PLLFREQR) & ECPM_PLLFREQ_MASK; uint8_t autocg; /* Disable auto gating and enable it by the respective module. */ autocg = sys_read8(config->ecpm_base + ECPM_AUTOCG); sys_write8(autocg & ~(ECPM_AUART1CG | ECPM_AUART2CG | ECPM_ASSPICG | ECPM_ACIRCG), config->ecpm_base + ECPM_AUTOCG); /* The following modules are gated in the initial state */ sys_write8(ECPM_CIRCG | ECPM_SWUCCG, config->ecpm_base + ECPM_CGCTRL2R); sys_write8(sys_read8(config->ecpm_base + ECPM_CGCTRL3R) | ECPM_PECICG | ECPM_SSPICG, config->ecpm_base + ECPM_CGCTRL3R); if (IS_ENABLED(CONFIG_ITE_IT51XXX_INTC)) { ite_intc_save_and_disable_interrupts(); } #ifdef CONFIG_ESPI /* * We have to disable eSPI pad before changing PLL sequence * or sequence will fail if CS# pin is low. */ espi_ite_ec_enable_pad_ctrl(ESPI_ITE_SOC_DEV, false); #endif if (reg_val != data->pll_configuration[config->pll_freq]) { /* configure PLL clock */ chip_configure_pll(dev, data->pll_configuration[config->pll_freq]); } #ifdef CONFIG_ESPI /* Enable eSPI pad after changing PLL sequence */ espi_ite_ec_enable_pad_ctrl(ESPI_ITE_SOC_DEV, true); #endif if (IS_ENABLED(CONFIG_ITE_IT51XXX_INTC)) { ite_intc_restore_interrupts(); } return 0; } /* Clock controller driver registration */ static DEVICE_API(clock_control, clock_control_it51xxx_api) = { .on = clock_control_it51xxx_on, .off = clock_control_it51xxx_off, .get_rate = clock_control_it51xxx_get_rate, }; static struct clock_control_it51xxx_data clock_control_it51xxx_data = { .pll_configuration = pll_cfg, }; static const struct clock_control_it51xxx_config clock_control_it51xxx_cfg = { .ecpm_base = DT_INST_REG_ADDR(0), .pll_freq = DT_INST_PROP(0, pll_frequency), }; DEVICE_DT_INST_DEFINE(0, clock_control_it51xxx_init, NULL, &clock_control_it51xxx_data, &clock_control_it51xxx_cfg, PRE_KERNEL_1, CONFIG_IT51XXX_PLL_SEQUENCE_PRIORITY, &clock_control_it51xxx_api);