xref: /openbmc/u-boot/arch/arm/mach-snapdragon/clock-snapdragon.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom 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 */
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 
337c75f7f1SJorge Ramirez-Ortiz void clk_enable_gpll0(phys_addr_t base, const struct gpll0_ctrl *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 
447c75f7f1SJorge Ramirez-Ortiz #define APPS_CMD_RGCR_UPDATE BIT(0)
457c75f7f1SJorge Ramirez-Ortiz 
467c75f7f1SJorge Ramirez-Ortiz /* Update clock command via CMD_RGCR */
477c75f7f1SJorge Ramirez-Ortiz void clk_bcr_update(phys_addr_t apps_cmd_rgcr)
487c75f7f1SJorge Ramirez-Ortiz {
497c75f7f1SJorge Ramirez-Ortiz 	setbits_le32(apps_cmd_rgcr, APPS_CMD_RGCR_UPDATE);
507c75f7f1SJorge Ramirez-Ortiz 
517c75f7f1SJorge Ramirez-Ortiz 	/* Wait for frequency to be updated. */
527c75f7f1SJorge Ramirez-Ortiz 	while (readl(apps_cmd_rgcr) & APPS_CMD_RGCR_UPDATE)
537c75f7f1SJorge Ramirez-Ortiz 		;
547c75f7f1SJorge Ramirez-Ortiz }
557c75f7f1SJorge Ramirez-Ortiz 
567c75f7f1SJorge Ramirez-Ortiz #define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */
577c75f7f1SJorge Ramirez-Ortiz 
587c75f7f1SJorge Ramirez-Ortiz #define CFG_MASK 0x3FFF
597c75f7f1SJorge Ramirez-Ortiz 
607c75f7f1SJorge Ramirez-Ortiz #define CFG_DIVIDER_MASK 0x1F
617c75f7f1SJorge Ramirez-Ortiz 
627c75f7f1SJorge Ramirez-Ortiz /* root set rate for clocks with half integer and MND divider */
637c75f7f1SJorge Ramirez-Ortiz void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs,
647c75f7f1SJorge Ramirez-Ortiz 			  int div, int m, int n, int source)
657c75f7f1SJorge Ramirez-Ortiz {
667c75f7f1SJorge Ramirez-Ortiz 	u32 cfg;
677c75f7f1SJorge Ramirez-Ortiz 	/* M value for MND divider. */
687c75f7f1SJorge Ramirez-Ortiz 	u32 m_val = m;
697c75f7f1SJorge Ramirez-Ortiz 	/* NOT(N-M) value for MND divider. */
707c75f7f1SJorge Ramirez-Ortiz 	u32 n_val = ~((n) - (m)) * !!(n);
717c75f7f1SJorge Ramirez-Ortiz 	/* NOT 2D value for MND divider. */
727c75f7f1SJorge Ramirez-Ortiz 	u32 d_val = ~(n);
737c75f7f1SJorge Ramirez-Ortiz 
747c75f7f1SJorge Ramirez-Ortiz 	/* Program MND values */
757c75f7f1SJorge Ramirez-Ortiz 	writel(m_val, base + regs->M);
767c75f7f1SJorge Ramirez-Ortiz 	writel(n_val, base + regs->N);
777c75f7f1SJorge Ramirez-Ortiz 	writel(d_val, base + regs->D);
787c75f7f1SJorge Ramirez-Ortiz 
797c75f7f1SJorge Ramirez-Ortiz 	/* setup src select and divider */
807c75f7f1SJorge Ramirez-Ortiz 	cfg  = readl(base + regs->cfg_rcgr);
817c75f7f1SJorge Ramirez-Ortiz 	cfg &= ~CFG_MASK;
827c75f7f1SJorge Ramirez-Ortiz 	cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */
837c75f7f1SJorge Ramirez-Ortiz 
847c75f7f1SJorge Ramirez-Ortiz 	/* Set the divider; HW permits fraction dividers (+0.5), but
857c75f7f1SJorge Ramirez-Ortiz 	   for simplicity, we will support integers only */
867c75f7f1SJorge Ramirez-Ortiz 	if (div)
877c75f7f1SJorge Ramirez-Ortiz 		cfg |= (2 * div - 1) & CFG_DIVIDER_MASK;
887c75f7f1SJorge Ramirez-Ortiz 
897c75f7f1SJorge Ramirez-Ortiz 	if (n_val)
907c75f7f1SJorge Ramirez-Ortiz 		cfg |= CFG_MODE_DUAL_EDGE;
917c75f7f1SJorge Ramirez-Ortiz 
927c75f7f1SJorge Ramirez-Ortiz 	writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */
937c75f7f1SJorge Ramirez-Ortiz 
947c75f7f1SJorge Ramirez-Ortiz 	/* Inform h/w to start using the new config. */
957c75f7f1SJorge Ramirez-Ortiz 	clk_bcr_update(base + regs->cmd_rcgr);
967c75f7f1SJorge Ramirez-Ortiz }
977c75f7f1SJorge Ramirez-Ortiz 
987c75f7f1SJorge Ramirez-Ortiz static int msm_clk_probe(struct udevice *dev)
997c75f7f1SJorge Ramirez-Ortiz {
1007c75f7f1SJorge Ramirez-Ortiz 	struct msm_clk_priv *priv = dev_get_priv(dev);
1017c75f7f1SJorge Ramirez-Ortiz 
1027c75f7f1SJorge Ramirez-Ortiz 	priv->base = devfdt_get_addr(dev);
1037c75f7f1SJorge Ramirez-Ortiz 	if (priv->base == FDT_ADDR_T_NONE)
1047c75f7f1SJorge Ramirez-Ortiz 		return -EINVAL;
1057c75f7f1SJorge Ramirez-Ortiz 
1067c75f7f1SJorge Ramirez-Ortiz 	return 0;
1077c75f7f1SJorge Ramirez-Ortiz }
1087c75f7f1SJorge Ramirez-Ortiz 
1097c75f7f1SJorge Ramirez-Ortiz static ulong msm_clk_set_rate(struct clk *clk, ulong rate)
1107c75f7f1SJorge Ramirez-Ortiz {
1117c75f7f1SJorge Ramirez-Ortiz 	return msm_set_rate(clk, rate);
1127c75f7f1SJorge Ramirez-Ortiz }
1137c75f7f1SJorge Ramirez-Ortiz 
1147c75f7f1SJorge Ramirez-Ortiz static struct clk_ops msm_clk_ops = {
1157c75f7f1SJorge Ramirez-Ortiz 	.set_rate = msm_clk_set_rate,
1167c75f7f1SJorge Ramirez-Ortiz };
1177c75f7f1SJorge Ramirez-Ortiz 
1187c75f7f1SJorge Ramirez-Ortiz static const struct udevice_id msm_clk_ids[] = {
1197c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-msm8916" },
1207c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-apq8016" },
1217c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-msm8996" },
1227c75f7f1SJorge Ramirez-Ortiz 	{ .compatible = "qcom,gcc-apq8096" },
1237c75f7f1SJorge Ramirez-Ortiz 	{ }
1247c75f7f1SJorge Ramirez-Ortiz };
1257c75f7f1SJorge Ramirez-Ortiz 
1267c75f7f1SJorge Ramirez-Ortiz U_BOOT_DRIVER(clk_msm) = {
1277c75f7f1SJorge Ramirez-Ortiz 	.name		= "clk_msm",
1287c75f7f1SJorge Ramirez-Ortiz 	.id		= UCLASS_CLK,
1297c75f7f1SJorge Ramirez-Ortiz 	.of_match	= msm_clk_ids,
1307c75f7f1SJorge Ramirez-Ortiz 	.ops		= &msm_clk_ops,
1317c75f7f1SJorge Ramirez-Ortiz 	.priv_auto_alloc_size = sizeof(struct msm_clk_priv),
1327c75f7f1SJorge Ramirez-Ortiz 	.probe		= msm_clk_probe,
1337c75f7f1SJorge Ramirez-Ortiz };
134