Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions core/arch/arm/plat-qcom/hoya/lemans/target.mk
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,12 @@ CFG_RESERVED_VASPACE_SIZE ?= (256 * 1024 * 1024)
CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe
endif
CFG_QCOM_HWKM ?= y

# QUPv3 serial-engine (bus) clock set-rate/DFS walker, consumed on-demand by a
# future TEE-side SPI/I2C driver. Set-rate votes CX/MX via RPMh, so pull
# cmd_db/RPMh client in whenever the walker is built.
CFG_QCOM_CLK_CFG ?= y
ifeq ($(CFG_QCOM_CLK_CFG),y)
$(call force,CFG_QCOM_CMD_DB,y)
$(call force,CFG_QCOM_RPMH_CLIENT,y)
endif
21 changes: 2 additions & 19 deletions core/drivers/clk/qcom/clock-qcom.c → core/drivers/clk/qcom/clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <drivers/clk_qcom.h>
#include <io.h>
#include <mm/core_mmu.h>
#include <trace.h>

register_phys_mem(MEM_AREA_IO_NSEC, GCC_BASE, GCC_SIZE);

Expand Down Expand Up @@ -50,22 +51,12 @@ register_phys_mem(MEM_AREA_IO_NSEC, GCC_BASE, GCC_SIZE);
/* PLL_USER_CTL_U fields */
#define PLL_USER_CTL_U_FINE_LOCK_DET BIT(0)

static inline bool cbcr_branch_on(uint32_t val)
{
return !(val & CBCR_BRANCH_OFF_BIT);
}

TEE_Result qcom_clock_enable_cbc(vaddr_t cbcr)
{
int ret = 0;

io_setbits32(cbcr, CBCR_BRANCH_ENABLE_BIT);

/*
* In hardware clock-control mode (HW_CTL set) CLK_OFF is driven by HW,
* not the software CLK_ENABLE write, so skip the poll to avoid
* spinning.
*/
if (io_read32(cbcr) & CBCR_HW_CTL_ENABLE_BIT)
return TEE_SUCCESS;

Expand All @@ -88,18 +79,15 @@ TEE_Result qcom_lucidevo_pll_enable(vaddr_t pll_base,
uint32_t user_val = 0;
int ret = 0;

/* Reg settings: program the static PLL trim/config registers. */
io_write32(pll_base + PLL_CONFIG_CTL, cfg->config_ctl);
io_write32(pll_base + PLL_CONFIG_CTL_U, cfg->config_ctl_u);
io_write32(pll_base + PLL_CONFIG_CTL_U1, cfg->config_ctl_u1);
io_write32(pll_base + PLL_USER_CTL, cfg->user_ctl);
io_write32(pll_base + PLL_USER_CTL_U, cfg->user_ctl_u);

/* ConfigPLL: program L value and fractional value. */
io_mask32(pll_base + PLL_L_VAL, cfg->l_val, PLL_L_VAL_L_MASK);
io_write32(pll_base + PLL_ALPHA_VAL, cfg->alpha_val);

/* Select fractional format and program the pre-/post-div ratios. */
user_val = io_read32(pll_base + PLL_USER_CTL);
if (cfg->frac_mode_mn)
user_val |= PLL_USER_CTL_FRAC_FORMAT_SEL;
Expand All @@ -115,24 +103,19 @@ TEE_Result qcom_lucidevo_pll_enable(vaddr_t pll_base,
PLL_USER_CTL_PRE_DIV_MASK;
io_write32(pll_base + PLL_USER_CTL, user_val);

/* Always use fine-grained lock detection. */
io_setbits32(pll_base + PLL_USER_CTL_U, PLL_USER_CTL_U_FINE_LOCK_DET);

/* SetCalConfig: program the calibration L value. */
io_mask32(pll_base + PLL_L_VAL,
SHIFT_U32(cfg->cal_l_val, PLL_L_VAL_CAL_L_SHIFT),
PLL_L_VAL_CAL_L_MASK);

/* Enable: select RUN opmode and take the PLL out of reset. */
io_write32(pll_base + PLL_OPMODE, PLL_OPMODE_RUN);
io_setbits32(pll_base + PLL_MODE, PLL_MODE_RESET_N);

/* Wait for the PLL to lock. */
REG_POLL_TIMEOUT(pll_base + PLL_MODE, 10 * 1000, 10, &ret, pll_locked);
if (ret < 0)
return TEE_ERROR_TIMEOUT;

/* Enable PLL outputs and the main output. */
io_setbits32(pll_base + PLL_MODE, PLL_MODE_OUTCTRL);
io_setbits32(pll_base + PLL_USER_CTL, PLL_USER_CTL_PLLOUT_MAIN_EN);

Expand Down Expand Up @@ -165,7 +148,7 @@ TEE_Result qcom_clock_enable(enum qcom_clk_group group)
case QCOM_CLKS_GPDSP1:
return qcom_clock_enable_pas(group);
default:
EMSG("Unsupported clock group %d\n", group);
EMSG("Unsupported clock group %d", group);
return TEE_ERROR_BAD_PARAMETERS;
}
}
82 changes: 82 additions & 0 deletions core/drivers/clk/qcom/clk_cfg.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries.
*
* Data contract for a platform's RCGs and branch clocks, consumed by the
* generic clock provider in clk_ops.c.
*/

#ifndef _CLK_CFG_H_
#define _CLK_CFG_H_

#include <mm/core_memprot.h>
#include <stdint.h>
#include <types_ext.h>

/* configs[] must be sorted ascending by freq_hz. */
struct clk_mux_config {
uint32_t freq_hz;
uint8_t src; /* software id, matched against parent_map[].src */
uint16_t div2x; /* 2x half-integer SRC_DIV; SRC_DIV = div2x - 1 */
uint32_t m;
uint32_t n; /* MND divider; used only when m && m < n */
uint8_t dfs_idx; /* DFS perf-state index, or CLK_DFS_NA */
uint16_t corner; /* RAIL_VOLTAGE_LEVEL_* this rate needs, or 0 */
};

#define CLK_DFS_NA 0xFF

/* One MMIO block a domain's registers live in, e.g. one GCC instance. */
struct clk_regmap {
struct io_pa_va io;
size_t size;
};

/* Maps one RCG's own hardware SRC_SEL encoding to a source clk. */
struct clk_parent_map {
uint8_t src; /* index into clk_cfg.pll_votes[] */
uint8_t mux_sel; /* this RCG's own SRC_SEL value for that source */
};

/* An RCG (mux/divider/DFS block), registered as its own struct clk. */
struct clk_rcg_desc {
const char *name;
struct clk_regmap *regmap;
paddr_t cmd_rcgr_addr;
uint16_t mnd_width; /* 0 if this RCG has no M/N/D hardware */
uint16_t dfs_states; /* DFS perf states supported; 0 means no DFS */
const struct clk_mux_config *configs;
uint32_t n_configs;
const struct clk_parent_map *parent_map;
uint8_t n_parents; /* number of entries in parent_map, also clk->parents[] count */
};

/* A CBCR branch gate, registered as its own struct clk. */
struct clk_branch_desc {
const char *name;
struct clk_regmap *regmap;
paddr_t cbcr_addr;
paddr_t clk_vote_addr; /* shared vote register; 0 for none */
uint8_t vote_bit;
const struct clk_rcg_desc *rcg; /* parent RCG, or NULL if branch-only */
};

/* One RCG source, registered as its own struct clk. Direct register write, not RPMh. */
struct clk_pll_vote {
const char *name;
struct clk_regmap *regmap;
paddr_t vote_reg_addr; /* 0 means enable/disable are no-ops (e.g. XO) */
uint8_t vote_bit;
};

/* Per-target config; combined branches point @rcg at a separate clk_rcg_desc. */
struct clk_cfg {
const struct clk_branch_desc *branches;
uint32_t n_branches;
const struct clk_pll_vote *pll_votes;
uint32_t n_pll_votes;
};

const struct clk_cfg *clk_get_cfg(void);

#endif /* _CLK_CFG_H_ */
Loading
Loading