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