From: Sam Day <[email protected]>

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 <[email protected]>
---
 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


Reply via email to