xref: /openbmc/u-boot/arch/arm/mach-snapdragon/clock-snapdragon.c (revision 640dc349422d1f228ef8d2bb35cd89b1663273f1)
183d290c5STom Rini // SPDX-License-Identifier: BSD-3-Clause
27c75f7f1SJorge Ramirez-Ortiz /*
37c75f7f1SJorge Ramirez-Ortiz  * Clock drivers for Qualcomm APQ8016, APQ8096
47c75f7f1SJorge Ramirez-Ortiz  *
57c75f7f1SJorge Ramirez-Ortiz  * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
67c75f7f1SJorge Ramirez-Ortiz  *
77c75f7f1SJorge Ramirez-Ortiz  * Based on Little Kernel driver, simplified
87c75f7f1SJorge Ramirez-Ortiz  */
97c75f7f1SJorge Ramirez-Ortiz 
107c75f7f1SJorge Ramirez-Ortiz #include <common.h>
117c75f7f1SJorge Ramirez-Ortiz #include <clk-uclass.h>
127c75f7f1SJorge Ramirez-Ortiz #include <dm.h>
137c75f7f1SJorge Ramirez-Ortiz #include <errno.h>
147c75f7f1SJorge Ramirez-Ortiz #include <asm/io.h>
157c75f7f1SJorge Ramirez-Ortiz #include <linux/bitops.h>
167c75f7f1SJorge Ramirez-Ortiz #include "clock-snapdragon.h"
177c75f7f1SJorge Ramirez-Ortiz 
187c75f7f1SJorge Ramirez-Ortiz /* CBCR register fields */
197c75f7f1SJorge Ramirez-Ortiz #define CBCR_BRANCH_ENABLE_BIT  BIT(0)
207c75f7f1SJorge Ramirez-Ortiz #define CBCR_BRANCH_OFF_BIT     BIT(31)
217c75f7f1SJorge Ramirez-Ortiz 
227c75f7f1SJorge Ramirez-Ortiz extern ulong msm_set_rate(struct clk *clk, ulong rate);
237c75f7f1SJorge Ramirez-Ortiz 
247c75f7f1SJorge Ramirez-Ortiz /* Enable clock controlled by CBC soft macro */
clk_enable_cbc(phys_addr_t cbcr)257c75f7f1SJorge Ramirez-Ortiz void clk_enable_cbc(phys_addr_t cbcr)
267c75f7f1SJorge Ramirez-Ortiz {
277c75f7f1SJorge Ramirez-Ortiz 	setbits_le32(cbcr, CBCR_BRANCH_ENABLE_BIT);
287c75f7f1SJorge Ramirez-Ortiz 
297c75f7f1SJorge Ramirez-Ortiz 	while (readl(cbcr) & CBCR_BRANCH_OFF_BIT)
307c75f7f1SJorge Ramirez-Ortiz 		;
317c75f7f1SJorge Ramirez-Ortiz }
327c75f7f1SJorge Ramirez-Ortiz 
clk_enable_gpll0(phys_addr_t base,const struct pll_vote_clk * gpll0)33*640dc349SRamon Fried void clk_enable_gpll0(phys_addr_t base, const struct pll_vote_clk *gpll0)
347c75f7f1SJorge Ramirez-Ortiz {
357c75f7f1SJorge Ramirez-Ortiz 	if (readl(base + gpll0->status) & gpll0->status_bit)
367c75f7f1SJorge Ramirez-Ortiz 		return; /* clock already enabled */
377c75f7f1SJorge Ramirez-Ortiz 
387c75f7f1SJorge Ramirez-Ortiz 	setbits_le32(base + gpll0->ena_vote, gpll0->vote_bit);
397c75f7f1SJorge Ramirez-Ortiz 
407c75f7f1SJorge Ramirez-Ortiz 	while ((readl(base + gpll0->status) & gpll0->status_bit) == 0)
417c75f7f1SJorge Ramirez-Ortiz 		;
427c75f7f1SJorge Ramirez-Ortiz }
437c75f7f1SJorge Ramirez-Ortiz 
44*640dc349SRamon Fried #define BRANCH_ON_VAL (0)
45*640dc349SRamon Fried #define BRANCH_NOC_FSM_ON_VAL BIT(29)
46*640dc349SRamon Fried #define BRANCH_CHECK_MASK GENMASK(31, 28)
47*640dc349SRamon Fried 
clk_enable_vote_clk(phys_addr_t base,const struct vote_clk * vclk)48*640dc349SRamon Fried void clk_enable_vote_clk(phys_addr_t base, const struct vote_clk *vclk)
49*640dc349SRamon Fried {
50*640dc349SRamon Fried 	u32 val;
51*640dc349SRamon Fried 
52*640dc349SRamon Fried 	setbits_le32(base + vclk->ena_vote, vclk->vote_bit);
53*640dc349SRamon Fried 	do {
54*640dc349SRamon Fried 		val = readl(base + vclk->cbcr_reg);
55*640dc349SRamon Fried 		val &= BRANCH_CHECK_MASK;
56*640dc349SRamon Fried 	} while ((val != BRANCH_ON_VAL) && (val != BRANCH_NOC_FSM_ON_VAL));
57*640dc349SRamon Fried }
58*640dc349SRamon Fried 
597c75f7f1SJorge Ramirez-Ortiz #define APPS_CMD_RGCR_UPDATE BIT(0)
607c75f7f1SJorge Ramirez-Ortiz 
617c75f7f1SJorge Ramirez-Ortiz /* Update clock command via CMD_RGCR */
clk_bcr_update(phys_addr_t apps_cmd_rgcr)627c75f7f1SJorge Ramirez-Ortiz void clk_bcr_update(phys_addr_t apps_cmd_rgcr)
637c75f7f1SJorge Ramirez-Ortiz {
647c75f7f1SJorge Ramirez-Ortiz 	setbits_le32(apps_cmd_rgcr, APPS_CMD_RGCR_UPDATE);
657c75f7f1SJorge Ramirez-Ortiz 
667c75f7f1SJorge Ramirez-Ortiz 	/* Wait for frequency to be updated. */
677c75f7f1SJorge Ramirez-Ortiz 	while (readl(apps_cmd_rgcr) & APPS_CMD_RGCR_UPDATE)
687c75f7f1SJorge Ramirez-Ortiz 		;
697c75f7f1SJorge Ramirez-Ortiz }
707c75f7f1SJorge Ramirez-Ortiz 
717c75f7f1SJorge Ramirez-Ortiz #define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */
727c75f7f1SJorge Ramirez-Ortiz 
737c75f7f1SJorge Ramirez-Ortiz #define CFG_MASK 0x3FFF
747c75f7f1SJorge Ramirez-Ortiz 
757c75f7f1SJorge Ramirez-Ortiz #define CFG_DIVIDER_MASK 0x1F
767c75f7f1SJorge Ramirez-Ortiz 
777c75f7f1SJorge Ramirez-Ortiz /* root set rate for clocks with half integer and MND divider */
clk_rcg_set_rate_mnd(phys_addr_t base,const struct bcr_regs * regs,int div,int m,int n,int source)787c75f7f1SJorge Ramirez-Ortiz void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
797c75f7f1SJorge Ramirez-Ortiz 			  int div, int m, int n, int source)
807c75f7f1SJorge Ramirez-Ortiz {
817c75f7f1SJorge Ramirez-Ortiz 	u32 cfg;
827c75f7f1SJorge Ramirez-Ortiz 	/* M value for MND divider. */
837c75f7f1SJorge Ramirez-Ortiz 	u32 m_val = m;
847c75f7f1SJorge Ramirez-Ortiz 	/* NOT(N-M) value for MND divider. */
857c75f7f1SJorge Ramirez-Ortiz 	u32 n_val = ~((n) - (m)) * !!(n);
867c75f7f1SJorge Ramirez-Ortiz 	/* NOT 2D value for MND divider. */
877c75f7f1SJorge Ramirez-Ortiz 	u32 d_val = ~(n);
887c75f7f1SJorge Ramirez-Ortiz 
897c75f7f1SJorge Ramirez-Ortiz 	/* Program MND values */
907c75f7f1SJorge Ramirez-Ortiz 	writel(m_val, base + regs->M);
917c75f7f1SJorge Ramirez-Ortiz 	writel(n_val, base + regs->N);
927c75f7f1SJorge Ramirez-Ortiz 	writel(d_val, base + regs->D);
937c75f7f1SJorge Ramirez-Ortiz 
947c75f7f1SJorge Ramirez-Ortiz 	/* setup src select and divider */
957c75f7f1SJorge Ramirez-Ortiz 	cfg  = readl(base + regs->cfg_rcgr);
967c75f7f1SJorge Ramirez-Ortiz 	cfg &= ~CFG_MASK;
977c75f7f1SJorge Ramirez-Ortiz 	cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
987c75f7f1SJorge Ramirez-Ortiz 
997c75f7f1SJorge Ramirez-Ortiz 	/* Set the divider; HW permits fraction dividers (+0.5), but
1007c75f7f1SJorge Ramirez-Ortiz 	   for simplicity, we will support integers only */
1017c75f7f1SJorge Ramirez-Ortiz 	if (div)
1027c75f7f1SJorge Ramirez-Ortiz 		cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
1037c75f7f1SJorge Ramirez-Ortiz 
1047c75f7f1SJorge Ramirez-Ortiz 	if (n_val)
1057c75f7f1SJorge Ramirez-Ortiz 		cfg |= CFG_MODE_DUAL_EDGE;
1067c75f7f1SJorge Ramirez-Ortiz 
1077c75f7f1SJorge Ramirez-Ortiz 	writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
1087c75f7f1SJorge Ramirez-Ortiz 
1097c75f7f1SJorge Ramirez-Ortiz 	/* Inform h/w to start using the new config. */
1107c75f7f1SJorge Ramirez-Ortiz 	clk_bcr_update(base + regs->cmd_rcgr);
1117c75f7f1SJorge Ramirez-Ortiz }
1127c75f7f1SJorge Ramirez-Ortiz 
msm_clk_probe(struct udevice * dev)1137c75f7f1SJorge Ramirez-Ortiz static int msm_clk_probe(struct udevice *dev)
1147c75f7f1SJorge Ramirez-Ortiz {
1157c75f7f1SJorge Ramirez-Ortiz 	struct msm_clk_priv *priv = dev_get_priv(dev);
1167c75f7f1SJorge Ramirez-Ortiz 
1177c75f7f1SJorge Ramirez-Ortiz 	priv->base = devfdt_get_addr(dev);
1187c75f7f1SJorge Ramirez-Ortiz 	if (priv->base == FDT_ADDR_T_NONE)
1197c75f7f1SJorge Ramirez-Ortiz 		return -EINVAL;
1207c75f7f1SJorge Ramirez-Ortiz 
1217c75f7f1SJorge Ramirez-Ortiz 	return 0;
1227c75f7f1SJorge Ramirez-Ortiz }
1237c75f7f1SJorge Ramirez-Ortiz 
msm_clk_set_rate(struct clk * clk,ulong rate)1247c75f7f1SJorge Ramirez-Ortiz static ulong msm_clk_set_rate(struct clk *clk, ulong rate)
1257c75f7f1SJorge Ramirez-Ortiz {
1267c75f7f1SJorge Ramirez-Ortiz 	return msm_set_rate(clk, rate);
1277c75f7f1SJorge Ramirez-Ortiz }
1287c75f7f1SJorge Ramirez-Ortiz 
1297c75f7f1SJorge Ramirez-Ortiz static struct clk_ops msm_clk_ops = {
1307c75f7f1SJorge Ramirez-Ortiz 	.set_rate = msm_clk_set_rate,
1317c75f7f1SJorge Ramirez-Ortiz };
1327c75f7f1SJorge Ramirez-Ortiz 
1337c75f7f1SJorge Ramirez-Ortiz static const struct udevice_id msm_clk_ids[] = {
1347c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-msm8916" },
1357c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-apq8016" },
1367c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-msm8996" },
1377c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-apq8096" },
1387c75f7f1SJorge Ramirez-Ortiz 	{ }
1397c75f7f1SJorge Ramirez-Ortiz };
1407c75f7f1SJorge Ramirez-Ortiz 
1417c75f7f1SJorge Ramirez-Ortiz U_BOOT_DRIVER(clk_msm) = {
1427c75f7f1SJorge Ramirez-Ortiz 	.name		= "clk_msm",
1437c75f7f1SJorge Ramirez-Ortiz 	.id		= UCLASS_CLK,
1447c75f7f1SJorge Ramirez-Ortiz 	.of_match	= msm_clk_ids,
1457c75f7f1SJorge Ramirez-Ortiz 	.ops		= &msm_clk_ops,
1467c75f7f1SJorge Ramirez-Ortiz 	.priv_auto_alloc_size = sizeof(struct msm_clk_priv),
1477c75f7f1SJorge Ramirez-Ortiz 	.probe		= msm_clk_probe,
1487c75f7f1SJorge Ramirez-Ortiz };
149