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