Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

215 lines
6.4 KiB

/*
* Copyright (c) 2025 ITE Corporation. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ite_it51xxx_ecpm
#include <soc.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/dt-bindings/clock/ite-it51xxx-clock.h>
#include <zephyr/dt-bindings/interrupt-controller/ite-it51xxx-intc.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
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);