clk/qcom: move from mach-snapdragon
Clock drivers don't belong here, move them to the right place and
declutter mach-snapdragon a bit.
To de-couple these drivers from specific "target" platforms, add
additional config options to enable each clock driver gated behind a
common CLK_QCOM option and enable them by default for the respective
targets. This will make future work easier as we move towards a generic
Qualcomm target.
Reviewed-by: Sumit Garg <sumit.garg@linaro.org>
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
new file mode 100644
index 0000000..a884f07
--- /dev/null
+++ b/drivers/clk/qcom/Kconfig
@@ -0,0 +1,44 @@
+if ARCH_SNAPDRAGON || ARCH_IPQ40XX
+
+config CLK_QCOM
+ bool
+ depends on CLK && DM_RESET
+ def_bool n
+
+menu "Qualcomm clock drivers"
+
+config CLK_QCOM_APQ8016
+ bool "Qualcomm APQ8016 GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon APQ8016 SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
+config CLK_QCOM_APQ8096
+ bool "Qualcomm APQ8096 GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon APQ8096 SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
+config CLK_QCOM_QCS404
+ bool "Qualcomm QCS404 GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon QCS404 SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
+config CLK_QCOM_SDM845
+ bool "Qualcomm SDM845 GCC"
+ select CLK_QCOM
+ help
+ Say Y here to enable support for the Global Clock Controller
+ on the Snapdragon 845 SoC. This driver supports the clocks
+ and resets exposed by the GCC hardware block.
+
+endmenu
+
+endif
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
new file mode 100644
index 0000000..44d5558
--- /dev/null
+++ b/drivers/clk/qcom/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2023 Linaro
+
+obj-y += clock-qcom.o
+obj-$(CONFIG_CLK_QCOM_SDM845) += clock-sdm845.o
+obj-$(CONFIG_CLK_QCOM_APQ8016) += clock-apq8016.o
+obj-$(CONFIG_CLK_QCOM_APQ8096) += clock-apq8096.o
+obj-$(CONFIG_CLK_QCOM_QCS404) += clock-qcs404.o
diff --git a/drivers/clk/qcom/clock-apq8016.c b/drivers/clk/qcom/clock-apq8016.c
new file mode 100644
index 0000000..90f2a93
--- /dev/null
+++ b/drivers/clk/qcom/clock-apq8016.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm APQ8016
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ *
+ * Based on Little Kernel driver, simplified
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include "clock-qcom.h"
+
+/* GPLL0 clock control registers */
+#define GPLL0_STATUS_ACTIVE BIT(17)
+
+static const struct bcr_regs sdc_regs[] = {
+ {
+ .cfg_rcgr = SDCC_CFG_RCGR(1),
+ .cmd_rcgr = SDCC_CMD_RCGR(1),
+ .M = SDCC_M(1),
+ .N = SDCC_N(1),
+ .D = SDCC_D(1),
+ },
+ {
+ .cfg_rcgr = SDCC_CFG_RCGR(2),
+ .cmd_rcgr = SDCC_CMD_RCGR(2),
+ .M = SDCC_M(2),
+ .N = SDCC_N(2),
+ .D = SDCC_D(2),
+ }
+};
+
+static struct pll_vote_clk gpll0_vote_clk = {
+ .status = GPLL0_STATUS,
+ .status_bit = GPLL0_STATUS_ACTIVE,
+ .ena_vote = APCS_GPLL_ENA_VOTE,
+ .vote_bit = BIT(0),
+};
+
+static struct vote_clk gcc_blsp1_ahb_clk = {
+ .cbcr_reg = BLSP1_AHB_CBCR,
+ .ena_vote = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .vote_bit = BIT(10),
+};
+
+/* SDHCI */
+static int clk_init_sdc(struct msm_clk_priv *priv, int slot, uint rate)
+{
+ int div = 8; /* 100MHz default */
+
+ if (rate == 200000000)
+ div = 4;
+
+ clk_enable_cbc(priv->base + SDCC_AHB_CBCR(slot));
+ /* 800Mhz/div, gpll0 */
+ clk_rcg_set_rate_mnd(priv->base, &sdc_regs[slot], div, 0, 0,
+ CFG_CLK_SRC_GPLL0);
+ clk_enable_gpll0(priv->base, &gpll0_vote_clk);
+ clk_enable_cbc(priv->base + SDCC_APPS_CBCR(slot));
+
+ return rate;
+}
+
+static const struct bcr_regs uart2_regs = {
+ .cfg_rcgr = BLSP1_UART2_APPS_CFG_RCGR,
+ .cmd_rcgr = BLSP1_UART2_APPS_CMD_RCGR,
+ .M = BLSP1_UART2_APPS_M,
+ .N = BLSP1_UART2_APPS_N,
+ .D = BLSP1_UART2_APPS_D,
+};
+
+/* UART: 115200 */
+static int clk_init_uart(struct msm_clk_priv *priv)
+{
+ /* Enable AHB clock */
+ clk_enable_vote_clk(priv->base, &gcc_blsp1_ahb_clk);
+
+ /* 7372800 uart block clock @ GPLL0 */
+ clk_rcg_set_rate_mnd(priv->base, &uart2_regs, 1, 144, 15625,
+ CFG_CLK_SRC_GPLL0);
+
+ /* Vote for gpll0 clock */
+ clk_enable_gpll0(priv->base, &gpll0_vote_clk);
+
+ /* Enable core clk */
+ clk_enable_cbc(priv->base + BLSP1_UART2_APPS_CBCR);
+
+ return 0;
+}
+
+ulong msm_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case 0: /* SDC1 */
+ return clk_init_sdc(priv, 0, rate);
+ break;
+ case 1: /* SDC2 */
+ return clk_init_sdc(priv, 1, rate);
+ break;
+ case 4: /* UART2 */
+ return clk_init_uart(priv);
+ break;
+ default:
+ return 0;
+ }
+}
+
+int msm_enable(struct clk *clk)
+{
+ return 0;
+}
diff --git a/drivers/clk/qcom/clock-apq8096.c b/drivers/clk/qcom/clock-apq8096.c
new file mode 100644
index 0000000..d5388c6
--- /dev/null
+++ b/drivers/clk/qcom/clock-apq8096.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm APQ8096
+ *
+ * (C) Copyright 2017 Jorge Ramirez Ortiz <jorge.ramirez-ortiz@linaro.org>
+ *
+ * Based on Little Kernel driver, simplified
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+
+#include "clock-qcom.h"
+
+/* GPLL0 clock control registers */
+#define GPLL0_STATUS_ACTIVE BIT(30)
+#define APCS_GPLL_ENA_VOTE_GPLL0 BIT(0)
+
+static const struct bcr_regs sdc_regs = {
+ .cfg_rcgr = SDCC2_CFG_RCGR,
+ .cmd_rcgr = SDCC2_CMD_RCGR,
+ .M = SDCC2_M,
+ .N = SDCC2_N,
+ .D = SDCC2_D,
+};
+
+static const struct pll_vote_clk gpll0_vote_clk = {
+ .status = GPLL0_STATUS,
+ .status_bit = GPLL0_STATUS_ACTIVE,
+ .ena_vote = APCS_GPLL_ENA_VOTE,
+ .vote_bit = APCS_GPLL_ENA_VOTE_GPLL0,
+};
+
+static struct vote_clk gcc_blsp2_ahb_clk = {
+ .cbcr_reg = BLSP2_AHB_CBCR,
+ .ena_vote = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .vote_bit = BIT(15),
+};
+
+static int clk_init_sdc(struct msm_clk_priv *priv, uint rate)
+{
+ int div = 3;
+
+ clk_enable_cbc(priv->base + SDCC2_AHB_CBCR);
+ clk_rcg_set_rate_mnd(priv->base, &sdc_regs, div, 0, 0,
+ CFG_CLK_SRC_GPLL0);
+ clk_enable_gpll0(priv->base, &gpll0_vote_clk);
+ clk_enable_cbc(priv->base + SDCC2_APPS_CBCR);
+
+ return rate;
+}
+
+static const struct bcr_regs uart2_regs = {
+ .cfg_rcgr = BLSP2_UART2_APPS_CFG_RCGR,
+ .cmd_rcgr = BLSP2_UART2_APPS_CMD_RCGR,
+ .M = BLSP2_UART2_APPS_M,
+ .N = BLSP2_UART2_APPS_N,
+ .D = BLSP2_UART2_APPS_D,
+};
+
+static int clk_init_uart(struct msm_clk_priv *priv)
+{
+ /* Enable AHB clock */
+ clk_enable_vote_clk(priv->base, &gcc_blsp2_ahb_clk);
+
+ /* 7372800 uart block clock @ GPLL0 */
+ clk_rcg_set_rate_mnd(priv->base, &uart2_regs, 1, 192, 15625,
+ CFG_CLK_SRC_GPLL0);
+
+ /* Vote for gpll0 clock */
+ clk_enable_gpll0(priv->base, &gpll0_vote_clk);
+
+ /* Enable core clk */
+ clk_enable_cbc(priv->base + BLSP2_UART2_APPS_CBCR);
+
+ return 0;
+}
+
+ulong msm_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case 0: /* SDC1 */
+ return clk_init_sdc(priv, rate);
+ break;
+ case 4: /*UART2*/
+ return clk_init_uart(priv);
+ default:
+ return 0;
+ }
+}
+
+int msm_enable(struct clk *clk)
+{
+ return 0;
+}
diff --git a/drivers/clk/qcom/clock-qcom.c b/drivers/clk/qcom/clock-qcom.c
new file mode 100644
index 0000000..5667abe
--- /dev/null
+++ b/drivers/clk/qcom/clock-qcom.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm APQ8016, APQ8096
+ *
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ *
+ * Based on Little Kernel driver, simplified
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include "clock-qcom.h"
+
+/* CBCR register fields */
+#define CBCR_BRANCH_ENABLE_BIT BIT(0)
+#define CBCR_BRANCH_OFF_BIT BIT(31)
+
+extern ulong msm_set_rate(struct clk *clk, ulong rate);
+extern int msm_enable(struct clk *clk);
+
+/* Enable clock controlled by CBC soft macro */
+void clk_enable_cbc(phys_addr_t cbcr)
+{
+ setbits_le32(cbcr, CBCR_BRANCH_ENABLE_BIT);
+
+ while (readl(cbcr) & CBCR_BRANCH_OFF_BIT)
+ ;
+}
+
+void clk_enable_gpll0(phys_addr_t base, const struct pll_vote_clk *gpll0)
+{
+ if (readl(base + gpll0->status) & gpll0->status_bit)
+ return; /* clock already enabled */
+
+ setbits_le32(base + gpll0->ena_vote, gpll0->vote_bit);
+
+ while ((readl(base + gpll0->status) & gpll0->status_bit) == 0)
+ ;
+}
+
+#define BRANCH_ON_VAL (0)
+#define BRANCH_NOC_FSM_ON_VAL BIT(29)
+#define BRANCH_CHECK_MASK GENMASK(31, 28)
+
+void clk_enable_vote_clk(phys_addr_t base, const struct vote_clk *vclk)
+{
+ u32 val;
+
+ setbits_le32(base + vclk->ena_vote, vclk->vote_bit);
+ do {
+ val = readl(base + vclk->cbcr_reg);
+ val &= BRANCH_CHECK_MASK;
+ } while ((val != BRANCH_ON_VAL) && (val != BRANCH_NOC_FSM_ON_VAL));
+}
+
+#define APPS_CMD_RCGR_UPDATE BIT(0)
+
+/* Update clock command via CMD_RCGR */
+void clk_bcr_update(phys_addr_t apps_cmd_rcgr)
+{
+ setbits_le32(apps_cmd_rcgr, APPS_CMD_RCGR_UPDATE);
+
+ /* Wait for frequency to be updated. */
+ while (readl(apps_cmd_rcgr) & APPS_CMD_RCGR_UPDATE)
+ ;
+}
+
+#define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */
+
+#define CFG_MASK 0x3FFF
+
+#define CFG_DIVIDER_MASK 0x1F
+
+/* root set rate for clocks with half integer and MND divider */
+void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
+ int div, int m, int n, int source)
+{
+ u32 cfg;
+ /* M value for MND divider. */
+ u32 m_val = m;
+ /* NOT(N-M) value for MND divider. */
+ u32 n_val = ~((n) - (m)) * !!(n);
+ /* NOT 2D value for MND divider. */
+ u32 d_val = ~(n);
+
+ /* Program MND values */
+ writel(m_val, base + regs->M);
+ writel(n_val, base + regs->N);
+ writel(d_val, base + regs->D);
+
+ /* setup src select and divider */
+ cfg = readl(base + regs->cfg_rcgr);
+ cfg &= ~CFG_MASK;
+ cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
+
+ /* Set the divider; HW permits fraction dividers (+0.5), but
+ for simplicity, we will support integers only */
+ if (div)
+ cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
+
+ if (n_val)
+ cfg |= CFG_MODE_DUAL_EDGE;
+
+ writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
+
+ /* Inform h/w to start using the new config. */
+ clk_bcr_update(base + regs->cmd_rcgr);
+}
+
+/* root set rate for clocks with half integer and mnd_width=0 */
+void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
+ int source)
+{
+ u32 cfg;
+
+ /* setup src select and divider */
+ cfg = readl(base + regs->cfg_rcgr);
+ cfg &= ~CFG_MASK;
+ cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
+
+ /*
+ * Set the divider; HW permits fraction dividers (+0.5), but
+ * for simplicity, we will support integers only
+ */
+ if (div)
+ cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
+
+ writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
+
+ /* Inform h/w to start using the new config. */
+ clk_bcr_update(base + regs->cmd_rcgr);
+}
+
+static int msm_clk_probe(struct udevice *dev)
+{
+ struct msm_clk_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_read_addr(dev);
+ if (priv->base == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ulong msm_clk_set_rate(struct clk *clk, ulong rate)
+{
+ return msm_set_rate(clk, rate);
+}
+
+static int msm_clk_enable(struct clk *clk)
+{
+ return msm_enable(clk);
+}
+
+static struct clk_ops msm_clk_ops = {
+ .set_rate = msm_clk_set_rate,
+ .enable = msm_clk_enable,
+};
+
+static const struct udevice_id msm_clk_ids[] = {
+ { .compatible = "qcom,gcc-msm8916" },
+ { .compatible = "qcom,gcc-apq8016" },
+ { .compatible = "qcom,gcc-msm8996" },
+ { .compatible = "qcom,gcc-apq8096" },
+ { .compatible = "qcom,gcc-sdm845" },
+ { .compatible = "qcom,gcc-qcs404" },
+ { }
+};
+
+U_BOOT_DRIVER(clk_msm) = {
+ .name = "clk_msm",
+ .id = UCLASS_CLK,
+ .of_match = msm_clk_ids,
+ .ops = &msm_clk_ops,
+ .priv_auto = sizeof(struct msm_clk_priv),
+ .probe = msm_clk_probe,
+};
diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h
new file mode 100644
index 0000000..c90bbef
--- /dev/null
+++ b/drivers/clk/qcom/clock-qcom.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Qualcomm APQ8016, APQ8096, SDM845
+ *
+ * (C) Copyright 2017 Jorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
+ */
+#ifndef _CLOCK_SNAPDRAGON_H
+#define _CLOCK_SNAPDRAGON_H
+
+#define CFG_CLK_SRC_CXO (0 << 8)
+#define CFG_CLK_SRC_GPLL0 (1 << 8)
+#define CFG_CLK_SRC_GPLL0_EVEN (6 << 8)
+#define CFG_CLK_SRC_MASK (7 << 8)
+
+struct pll_vote_clk {
+ uintptr_t status;
+ int status_bit;
+ uintptr_t ena_vote;
+ int vote_bit;
+};
+
+struct vote_clk {
+ uintptr_t cbcr_reg;
+ uintptr_t ena_vote;
+ int vote_bit;
+};
+struct bcr_regs {
+ uintptr_t cfg_rcgr;
+ uintptr_t cmd_rcgr;
+ uintptr_t M;
+ uintptr_t N;
+ uintptr_t D;
+};
+
+struct msm_clk_priv {
+ phys_addr_t base;
+};
+
+void clk_enable_gpll0(phys_addr_t base, const struct pll_vote_clk *gpll0);
+void clk_bcr_update(phys_addr_t apps_cmd_rgcr);
+void clk_enable_cbc(phys_addr_t cbcr);
+void clk_enable_vote_clk(phys_addr_t base, const struct vote_clk *vclk);
+void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
+ int div, int m, int n, int source);
+void clk_rcg_set_rate(phys_addr_t base, const struct bcr_regs *regs, int div,
+ int source);
+
+#endif
diff --git a/drivers/clk/qcom/clock-qcs404.c b/drivers/clk/qcom/clock-qcs404.c
new file mode 100644
index 0000000..80218af
--- /dev/null
+++ b/drivers/clk/qcom/clock-qcs404.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm QCS404
+ *
+ * (C) Copyright 2022 Sumit Garg <sumit.garg@linaro.org>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include "clock-qcom.h"
+
+#include <dt-bindings/clock/qcom,gcc-qcs404.h>
+
+/* GPLL0 clock control registers */
+#define GPLL0_STATUS_ACTIVE BIT(31)
+
+#define CFG_CLK_SRC_GPLL1 BIT(8)
+#define GPLL1_STATUS_ACTIVE BIT(31)
+
+static struct vote_clk gcc_blsp1_ahb_clk = {
+ .cbcr_reg = BLSP1_AHB_CBCR,
+ .ena_vote = APCS_CLOCK_BRANCH_ENA_VOTE,
+ .vote_bit = BIT(10) | BIT(5) | BIT(4),
+};
+
+static const struct bcr_regs uart2_regs = {
+ .cfg_rcgr = BLSP1_UART2_APPS_CFG_RCGR,
+ .cmd_rcgr = BLSP1_UART2_APPS_CMD_RCGR,
+ .M = BLSP1_UART2_APPS_M,
+ .N = BLSP1_UART2_APPS_N,
+ .D = BLSP1_UART2_APPS_D,
+};
+
+static const struct bcr_regs sdc_regs = {
+ .cfg_rcgr = SDCC_CFG_RCGR(1),
+ .cmd_rcgr = SDCC_CMD_RCGR(1),
+ .M = SDCC_M(1),
+ .N = SDCC_N(1),
+ .D = SDCC_D(1),
+};
+
+static struct pll_vote_clk gpll0_vote_clk = {
+ .status = GPLL0_STATUS,
+ .status_bit = GPLL0_STATUS_ACTIVE,
+ .ena_vote = APCS_GPLL_ENA_VOTE,
+ .vote_bit = BIT(0),
+};
+
+static struct pll_vote_clk gpll1_vote_clk = {
+ .status = GPLL1_STATUS,
+ .status_bit = GPLL1_STATUS_ACTIVE,
+ .ena_vote = APCS_GPLL_ENA_VOTE,
+ .vote_bit = BIT(1),
+};
+
+static const struct bcr_regs usb30_master_regs = {
+ .cfg_rcgr = USB30_MASTER_CFG_RCGR,
+ .cmd_rcgr = USB30_MASTER_CMD_RCGR,
+ .M = USB30_MASTER_M,
+ .N = USB30_MASTER_N,
+ .D = USB30_MASTER_D,
+};
+
+static const struct bcr_regs emac_regs = {
+ .cfg_rcgr = EMAC_CFG_RCGR,
+ .cmd_rcgr = EMAC_CMD_RCGR,
+ .M = EMAC_M,
+ .N = EMAC_N,
+ .D = EMAC_D,
+};
+
+static const struct bcr_regs emac_ptp_regs = {
+ .cfg_rcgr = EMAC_PTP_CFG_RCGR,
+ .cmd_rcgr = EMAC_PTP_CMD_RCGR,
+ .M = EMAC_M,
+ .N = EMAC_N,
+ .D = EMAC_D,
+};
+
+static const struct bcr_regs blsp1_qup0_i2c_apps_regs = {
+ .cmd_rcgr = BLSP1_QUP0_I2C_APPS_CMD_RCGR,
+ .cfg_rcgr = BLSP1_QUP0_I2C_APPS_CFG_RCGR,
+ /* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup1_i2c_apps_regs = {
+ .cmd_rcgr = BLSP1_QUP1_I2C_APPS_CMD_RCGR,
+ .cfg_rcgr = BLSP1_QUP1_I2C_APPS_CFG_RCGR,
+ /* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup2_i2c_apps_regs = {
+ .cmd_rcgr = BLSP1_QUP2_I2C_APPS_CMD_RCGR,
+ .cfg_rcgr = BLSP1_QUP2_I2C_APPS_CFG_RCGR,
+ /* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup3_i2c_apps_regs = {
+ .cmd_rcgr = BLSP1_QUP3_I2C_APPS_CMD_RCGR,
+ .cfg_rcgr = BLSP1_QUP3_I2C_APPS_CFG_RCGR,
+ /* mnd_width = 0 */
+};
+
+static const struct bcr_regs blsp1_qup4_i2c_apps_regs = {
+ .cmd_rcgr = BLSP1_QUP4_I2C_APPS_CMD_RCGR,
+ .cfg_rcgr = BLSP1_QUP4_I2C_APPS_CFG_RCGR,
+ /* mnd_width = 0 */
+};
+
+ulong msm_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GCC_BLSP1_UART2_APPS_CLK:
+ /* UART: 115200 */
+ clk_rcg_set_rate_mnd(priv->base, &uart2_regs, 0, 12, 125,
+ CFG_CLK_SRC_CXO);
+ clk_enable_cbc(priv->base + BLSP1_UART2_APPS_CBCR);
+ break;
+ case GCC_BLSP1_AHB_CLK:
+ clk_enable_vote_clk(priv->base, &gcc_blsp1_ahb_clk);
+ break;
+ case GCC_SDCC1_APPS_CLK:
+ /* SDCC1: 200MHz */
+ clk_rcg_set_rate_mnd(priv->base, &sdc_regs, 4, 0, 0,
+ CFG_CLK_SRC_GPLL0);
+ clk_enable_gpll0(priv->base, &gpll0_vote_clk);
+ clk_enable_cbc(priv->base + SDCC_APPS_CBCR(1));
+ break;
+ case GCC_SDCC1_AHB_CLK:
+ clk_enable_cbc(priv->base + SDCC_AHB_CBCR(1));
+ break;
+ case GCC_ETH_RGMII_CLK:
+ if (rate == 250000000)
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 0, 0,
+ CFG_CLK_SRC_GPLL1);
+ else if (rate == 125000000)
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 4, 0, 0,
+ CFG_CLK_SRC_GPLL1);
+ else if (rate == 50000000)
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 10, 0, 0,
+ CFG_CLK_SRC_GPLL1);
+ else if (rate == 5000000)
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 1, 50,
+ CFG_CLK_SRC_GPLL1);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+int msm_enable(struct clk *clk)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GCC_USB30_MASTER_CLK:
+ clk_enable_cbc(priv->base + USB30_MASTER_CBCR);
+ clk_rcg_set_rate_mnd(priv->base, &usb30_master_regs, 4, 0, 0,
+ CFG_CLK_SRC_GPLL0);
+ break;
+ case GCC_SYS_NOC_USB3_CLK:
+ clk_enable_cbc(priv->base + SYS_NOC_USB3_CBCR);
+ break;
+ case GCC_USB30_SLEEP_CLK:
+ clk_enable_cbc(priv->base + USB30_SLEEP_CBCR);
+ break;
+ case GCC_USB30_MOCK_UTMI_CLK:
+ clk_enable_cbc(priv->base + USB30_MOCK_UTMI_CBCR);
+ break;
+ case GCC_USB_HS_PHY_CFG_AHB_CLK:
+ clk_enable_cbc(priv->base + USB_HS_PHY_CFG_AHB_CBCR);
+ break;
+ case GCC_USB2A_PHY_SLEEP_CLK:
+ clk_enable_cbc(priv->base + USB_HS_PHY_CFG_AHB_CBCR);
+ break;
+ case GCC_ETH_PTP_CLK:
+ /* SPEED_1000: freq -> 250MHz */
+ clk_enable_cbc(priv->base + ETH_PTP_CBCR);
+ clk_enable_gpll0(priv->base, &gpll1_vote_clk);
+ clk_rcg_set_rate_mnd(priv->base, &emac_ptp_regs, 2, 0, 0,
+ CFG_CLK_SRC_GPLL1);
+ break;
+ case GCC_ETH_RGMII_CLK:
+ /* SPEED_1000: freq -> 250MHz */
+ clk_enable_cbc(priv->base + ETH_RGMII_CBCR);
+ clk_enable_gpll0(priv->base, &gpll1_vote_clk);
+ clk_rcg_set_rate_mnd(priv->base, &emac_regs, 2, 0, 0,
+ CFG_CLK_SRC_GPLL1);
+ break;
+ case GCC_ETH_SLAVE_AHB_CLK:
+ clk_enable_cbc(priv->base + ETH_SLAVE_AHB_CBCR);
+ break;
+ case GCC_ETH_AXI_CLK:
+ clk_enable_cbc(priv->base + ETH_AXI_CBCR);
+ break;
+ case GCC_BLSP1_AHB_CLK:
+ clk_enable_vote_clk(priv->base, &gcc_blsp1_ahb_clk);
+ break;
+ case GCC_BLSP1_QUP0_I2C_APPS_CLK:
+ clk_enable_cbc(priv->base + BLSP1_QUP0_I2C_APPS_CBCR);
+ clk_rcg_set_rate(priv->base, &blsp1_qup0_i2c_apps_regs, 0,
+ CFG_CLK_SRC_CXO);
+ break;
+ case GCC_BLSP1_QUP1_I2C_APPS_CLK:
+ clk_enable_cbc(priv->base + BLSP1_QUP1_I2C_APPS_CBCR);
+ clk_rcg_set_rate(priv->base, &blsp1_qup1_i2c_apps_regs, 0,
+ CFG_CLK_SRC_CXO);
+ break;
+ case GCC_BLSP1_QUP2_I2C_APPS_CLK:
+ clk_enable_cbc(priv->base + BLSP1_QUP2_I2C_APPS_CBCR);
+ clk_rcg_set_rate(priv->base, &blsp1_qup2_i2c_apps_regs, 0,
+ CFG_CLK_SRC_CXO);
+ break;
+ case GCC_BLSP1_QUP3_I2C_APPS_CLK:
+ clk_enable_cbc(priv->base + BLSP1_QUP3_I2C_APPS_CBCR);
+ clk_rcg_set_rate(priv->base, &blsp1_qup3_i2c_apps_regs, 0,
+ CFG_CLK_SRC_CXO);
+ break;
+ case GCC_BLSP1_QUP4_I2C_APPS_CLK:
+ clk_enable_cbc(priv->base + BLSP1_QUP4_I2C_APPS_CBCR);
+ clk_rcg_set_rate(priv->base, &blsp1_qup4_i2c_apps_regs, 0,
+ CFG_CLK_SRC_CXO);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/drivers/clk/qcom/clock-sdm845.c b/drivers/clk/qcom/clock-sdm845.c
new file mode 100644
index 0000000..95a057b
--- /dev/null
+++ b/drivers/clk/qcom/clock-sdm845.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Clock drivers for Qualcomm SDM845
+ *
+ * (C) Copyright 2017 Jorge Ramirez Ortiz <jorge.ramirez-ortiz@linaro.org>
+ * (C) Copyright 2021 Dzmitry Sankouski <dsankouski@gmail.com>
+ *
+ * Based on Little Kernel driver, simplified
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include "clock-qcom.h"
+
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
+struct freq_tbl {
+ uint freq;
+ uint src;
+ u8 pre_div;
+ u16 m;
+ u16 n;
+};
+
+static const struct freq_tbl ftbl_gcc_qupv3_wrap0_s0_clk_src[] = {
+ F(7372800, CFG_CLK_SRC_GPLL0_EVEN, 1, 384, 15625),
+ F(14745600, CFG_CLK_SRC_GPLL0_EVEN, 1, 768, 15625),
+ F(19200000, CFG_CLK_SRC_CXO, 1, 0, 0),
+ F(29491200, CFG_CLK_SRC_GPLL0_EVEN, 1, 1536, 15625),
+ F(32000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 8, 75),
+ F(48000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 4, 25),
+ F(64000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 16, 75),
+ F(80000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 4, 15),
+ F(96000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 8, 25),
+ F(100000000, CFG_CLK_SRC_GPLL0_EVEN, 3, 0, 0),
+ F(102400000, CFG_CLK_SRC_GPLL0_EVEN, 1, 128, 375),
+ F(112000000, CFG_CLK_SRC_GPLL0_EVEN, 1, 28, 75),
+ F(117964800, CFG_CLK_SRC_GPLL0_EVEN, 1, 6144, 15625),
+ F(120000000, CFG_CLK_SRC_GPLL0_EVEN, 2.5, 0, 0),
+ F(128000000, CFG_CLK_SRC_GPLL0, 1, 16, 75),
+ { }
+};
+
+static const struct bcr_regs uart2_regs = {
+ .cfg_rcgr = SE9_UART_APPS_CFG_RCGR,
+ .cmd_rcgr = SE9_UART_APPS_CMD_RCGR,
+ .M = SE9_UART_APPS_M,
+ .N = SE9_UART_APPS_N,
+ .D = SE9_UART_APPS_D,
+};
+
+const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, uint rate)
+{
+ if (!f)
+ return NULL;
+
+ if (!f->freq)
+ return f;
+
+ for (; f->freq; f++)
+ if (rate <= f->freq)
+ return f;
+
+ /* Default to our fastest rate */
+ return f - 1;
+}
+
+static int clk_init_uart(struct msm_clk_priv *priv, uint rate)
+{
+ const struct freq_tbl *freq = qcom_find_freq(ftbl_gcc_qupv3_wrap0_s0_clk_src, rate);
+
+ clk_rcg_set_rate_mnd(priv->base, &uart2_regs,
+ freq->pre_div, freq->m, freq->n, freq->src);
+
+ return 0;
+}
+
+ulong msm_set_rate(struct clk *clk, ulong rate)
+{
+ struct msm_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case GCC_QUPV3_WRAP1_S1_CLK: /*UART2*/
+ return clk_init_uart(priv, rate);
+ default:
+ return 0;
+ }
+}
+
+int msm_enable(struct clk *clk)
+{
+ return 0;
+}