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;
+}