[PATCH RESEND 13/16] clk: qcom: MSM8960 GCC driver
Sam Day via B4 Relay
devnull+me.samcday.com at kernel.org
Mon Jun 1 10:12:54 CEST 2026
From: Sam Day <me at samcday.com>
This older GCC does not use RCGR/CBCR, but rather NS/MD/HALT registers.
We can't benefit as much from shared qcom clk infra, nor the `clk dump`
helpers (which assume newer RCGR/PLL register layout).
We start with just the UART clocks.
Signed-off-by: Sam Day <me at samcday.com>
---
configs/qcom_armv7_defconfig | 1 +
drivers/clk/qcom/Kconfig | 8 ++
drivers/clk/qcom/Makefile | 1 +
drivers/clk/qcom/clock-msm8960.c | 189 +++++++++++++++++++++++++++++++++++++++
4 files changed, 199 insertions(+)
diff --git a/configs/qcom_armv7_defconfig b/configs/qcom_armv7_defconfig
index aef8838c8a8..91525138ec9 100644
--- a/configs/qcom_armv7_defconfig
+++ b/configs/qcom_armv7_defconfig
@@ -34,6 +34,7 @@ CONFIG_OF_UPSTREAM_BUILD_VENDOR=y
CONFIG_ENV_USE_DEFAULT_ENV_TEXT_FILE=y
CONFIG_ENV_DEFAULT_ENV_TEXT_FILE="board/qualcomm/default.env"
CONFIG_CLK=y
+CONFIG_CLK_QCOM_MSM8960=y
CONFIG_USB_FUNCTION_FASTBOOT=y
CONFIG_FASTBOOT_BUF_ADDR=0x0
CONFIG_FASTBOOT_FLASH=y
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index 0a2ce55aaa2..3526a4c218b 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -55,6 +55,14 @@ config CLK_QCOM_MILOS
on the Snapdragon Milos SoC. This driver supports the clocks
and resets exposed by the GCC hardware block.
+config CLK_QCOM_MSM8960
+ bool "Qualcomm MSM8960 GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon MSM8960 SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
config CLK_QCOM_QCM2290
bool "Qualcomm QCM2290 GCC"
select CLK_QCOM
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index b96d61b603e..60a39a7cb39 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_CLK_QCOM_IPQ4019) += clock-ipq4019.o
obj-$(CONFIG_CLK_QCOM_IPQ5424) += clock-ipq5424.o
obj-$(CONFIG_CLK_QCOM_IPQ9574) += clock-ipq9574.o
obj-$(CONFIG_CLK_QCOM_MILOS) += clock-milos.o
+obj-$(CONFIG_CLK_QCOM_MSM8960) += clock-msm8960.o
obj-$(CONFIG_CLK_QCOM_QCM2290) += clock-qcm2290.o
obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o
obj-$(CONFIG_CLK_QCOM_QCS8300) += clock-qcs8300.o
diff --git a/drivers/clk/qcom/clock-msm8960.c b/drivers/clk/qcom/clock-msm8960.c
new file mode 100644
index 00000000000..b17fc48fd02
--- /dev/null
+++ b/drivers/clk/qcom/clock-msm8960.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <dt-bindings/clock/qcom,gcc-msm8960.h>
+
+#include "clock-qcom.h"
+
+#define GPLL8_STATUS 0x3158
+#define APCS_GPLL_ENA_VOTE 0x34c0
+#define GPLL8_STATUS_ACTIVE BIT(16)
+
+#define GSBI5_H_NS 0x2a40
+#define GSBI5_UART_MD 0x2a50
+#define GSBI5_UART_NS 0x2a54
+#define GSBI5_HALT 0x2fd0
+
+#define GSBI5_UART_HALT_BIT 22
+#define GSBI5_H_HALT_BIT 23
+
+#define GSBI_H_CLK_EN BIT(4)
+#define GSBI_H_HWC_EN BIT(6)
+#define GSBI_UART_CLK_EN BIT(9)
+#define GSBI_UART_SRC_EN BIT(11)
+
+#define RCG_SRC_SEL_MASK GENMASK(2, 0)
+#define RCG_PRE_DIV_MASK GENMASK(4, 3)
+#define RCG_MN_MODE_MASK GENMASK(6, 5)
+#define RCG_MN_MODE_DUAL (2 << 5)
+#define RCG_MN_RESET BIT(7)
+#define RCG_MN_EN BIT(8)
+#define RCG_MND_MASK GENMASK(15, 0)
+#define RCG_N_VAL_SHIFT 16
+#define RCG_M_VAL_SHIFT 16
+#define RCG_PRE_DIV_SHIFT 3
+
+#define MSM8960_SRC_PLL8 3
+
+static const struct pll_vote_clk pll8_vote_clk = {
+ .status = GPLL8_STATUS,
+ .status_bit = GPLL8_STATUS_ACTIVE,
+ .ena_vote = APCS_GPLL_ENA_VOTE,
+ .vote_bit = BIT(8),
+};
+
+static const struct freq_tbl gsbi_uart_freqs[] = {
+ { 1843200, MSM8960_SRC_PLL8, 2, 6, 625 },
+ { 3686400, MSM8960_SRC_PLL8, 2, 12, 625 },
+ { 7372800, MSM8960_SRC_PLL8, 2, 24, 625 },
+ { 14745600, MSM8960_SRC_PLL8, 2, 48, 625 },
+ { }
+};
+
+static const struct gate_clk msm8960_clks[] = {
+ GATE_CLK(GSBI5_H_CLK, GSBI5_H_NS, GSBI_H_CLK_EN),
+ GATE_CLK(GSBI5_UART_CLK, GSBI5_UART_NS, GSBI_UART_CLK_EN),
+};
+
+static int msm8960_branch_wait(phys_addr_t base, unsigned int halt_bit)
+{
+ u32 count;
+
+ for (count = 0; count < 200; count++) {
+ if (!(readl(base + GSBI5_HALT) & BIT(halt_bit)))
+ return 0;
+ udelay(1);
+ }
+
+ log_warning("MSM8960 GCC branch clock %u stuck off\n", halt_bit);
+ return -EBUSY;
+}
+
+static int msm8960_enable_gsbi5_h_clk(phys_addr_t base)
+{
+ setbits_le32(base + GSBI5_H_NS, GSBI_H_CLK_EN);
+
+ if (readl(base + GSBI5_H_NS) & GSBI_H_HWC_EN)
+ return 0;
+
+ return msm8960_branch_wait(base, GSBI5_H_HALT_BIT);
+}
+
+static void msm8960_rcg_set_rate(phys_addr_t base, const struct freq_tbl *freq)
+{
+ u32 md;
+ u32 ns;
+
+ setbits_le32(base + GSBI5_UART_NS, RCG_MN_RESET);
+
+ md = (freq->m << RCG_M_VAL_SHIFT) | (~freq->n & RCG_MND_MASK);
+ writel(md, base + GSBI5_UART_MD);
+
+ ns = readl(base + GSBI5_UART_NS);
+ ns &= ~(RCG_SRC_SEL_MASK | RCG_PRE_DIV_MASK | RCG_MN_MODE_MASK |
+ RCG_MN_EN | (RCG_MND_MASK << RCG_N_VAL_SHIFT));
+
+ if (freq->n) {
+ ns |= RCG_MN_EN | RCG_MN_MODE_DUAL;
+ ns |= (~(freq->n - freq->m) & RCG_MND_MASK) << RCG_N_VAL_SHIFT;
+ }
+
+ ns |= (freq->pre_div - 1) << RCG_PRE_DIV_SHIFT;
+ ns |= freq->src;
+ writel(ns, base + GSBI5_UART_NS);
+
+ clrbits_le32(base + GSBI5_UART_NS, RCG_MN_RESET);
+}
+
+static ulong msm8960_gsbi5_uart_set_rate(struct msm_clk_priv *priv, ulong rate)
+{
+ const struct freq_tbl *freq = qcom_find_freq(gsbi_uart_freqs, rate);
+ int ret;
+
+ if (!freq || !freq->freq)
+ return 0;
+
+ clk_enable_gpll0(priv->base, &pll8_vote_clk);
+ msm8960_rcg_set_rate(priv->base, freq);
+
+ setbits_le32(priv->base + GSBI5_UART_NS, GSBI_UART_SRC_EN |
+ GSBI_UART_CLK_EN);
+ ret = msm8960_branch_wait(priv->base, GSBI5_UART_HALT_BIT);
+ if (ret)
+ return 0;
+
+ ret = msm8960_enable_gsbi5_h_clk(priv->base);
+ if (ret)
+ return 0;
+
+ return freq->freq;
+}
+
+static ulong msm8960_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GSBI5_UART_SRC:
+ case GSBI5_UART_CLK:
+ return msm8960_gsbi5_uart_set_rate(priv, rate);
+ default:
+ return 0;
+ }
+}
+
+static int msm8960_clk_enable(struct clk *clk)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GSBI5_H_CLK:
+ return msm8960_enable_gsbi5_h_clk(priv->base);
+ case GSBI5_UART_SRC:
+ setbits_le32(priv->base + GSBI5_UART_NS, GSBI_UART_SRC_EN);
+ return 0;
+ case GSBI5_UART_CLK:
+ setbits_le32(priv->base + GSBI5_UART_NS, GSBI_UART_CLK_EN);
+ return msm8960_branch_wait(priv->base, GSBI5_UART_HALT_BIT);
+ default:
+ return -ENOENT;
+ }
+}
+
+static struct msm_clk_data msm8960_clk_data = {
+ .clks = msm8960_clks,
+ .num_clks = ARRAY_SIZE(msm8960_clks),
+ .set_rate = msm8960_clk_set_rate,
+ .enable = msm8960_clk_enable,
+};
+
+static const struct udevice_id gcc_msm8960_of_match[] = {
+ {
+ .compatible = "qcom,gcc-msm8960",
+ .data = (ulong)&msm8960_clk_data,
+ },
+ { }
+};
+
+U_BOOT_DRIVER(gcc_msm8960) = {
+ .name = "gcc_msm8960",
+ .id = UCLASS_NOP,
+ .of_match = gcc_msm8960_of_match,
+ .bind = qcom_cc_bind,
+ .flags = DM_FLAG_PRE_RELOC,
+};
--
2.54.0
More information about the U-Boot
mailing list