1 // SPDX-License-Identifier: BSD-3-Clause 2 /* 3 * Clock drivers for Qualcomm APQ8016, APQ8096 4 * 5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> 6 * 7 * Based on Little Kernel driver, simplified 8 */ 9 10 #include <common.h> 11 #include <clk-uclass.h> 12 #include <dm.h> 13 #include <errno.h> 14 #include <asm/io.h> 15 #include <linux/bitops.h> 16 #include "clock-snapdragon.h" 17 18 /* CBCR register fields */ 19 #define CBCR_BRANCH_ENABLE_BIT BIT(0) 20 #define CBCR_BRANCH_OFF_BIT BIT(31) 21 22 extern ulong msm_set_rate(struct clk *clk, ulong rate); 23 24 /* Enable clock controlled by CBC soft macro */ 25 void clk_enable_cbc(phys_addr_t cbcr) 26 { 27 setbits_le32(cbcr, CBCR_BRANCH_ENABLE_BIT); 28 29 while (readl(cbcr) & CBCR_BRANCH_OFF_BIT) 30 ; 31 } 32 33 void clk_enable_gpll0(phys_addr_t base, const struct gpll0_ctrl *gpll0) 34 { 35 if (readl(base + gpll0->status) & gpll0->status_bit) 36 return; /* clock already enabled */ 37 38 setbits_le32(base + gpll0->ena_vote, gpll0->vote_bit); 39 40 while ((readl(base + gpll0->status) & gpll0->status_bit) == 0) 41 ; 42 } 43 44 #define APPS_CMD_RGCR_UPDATE BIT(0) 45 46 /* Update clock command via CMD_RGCR */ 47 void clk_bcr_update(phys_addr_t apps_cmd_rgcr) 48 { 49 setbits_le32(apps_cmd_rgcr, APPS_CMD_RGCR_UPDATE); 50 51 /* Wait for frequency to be updated. */ 52 while (readl(apps_cmd_rgcr) & APPS_CMD_RGCR_UPDATE) 53 ; 54 } 55 56 #define CFG_MODE_DUAL_EDGE (0x2 << 12) /* Counter mode */ 57 58 #define CFG_MASK 0x3FFF 59 60 #define CFG_DIVIDER_MASK 0x1F 61 62 /* root set rate for clocks with half integer and MND divider */ 63 void clk_rcg_set_rate_mnd(phys_addr_t base, const struct bcr_regs *regs, 64 int div, int m, int n, int source) 65 { 66 u32 cfg; 67 /* M value for MND divider. */ 68 u32 m_val = m; 69 /* NOT(N-M) value for MND divider. */ 70 u32 n_val = ~((n) - (m)) * !!(n); 71 /* NOT 2D value for MND divider. */ 72 u32 d_val = ~(n); 73 74 /* Program MND values */ 75 writel(m_val, base + regs->M); 76 writel(n_val, base + regs->N); 77 writel(d_val, base + regs->D); 78 79 /* setup src select and divider */ 80 cfg = readl(base + regs->cfg_rcgr); 81 cfg &= ~CFG_MASK; 82 cfg |= source & CFG_CLK_SRC_MASK; /* Select clock source */ 83 84 /* Set the divider; HW permits fraction dividers (+0.5), but 85 for simplicity, we will support integers only */ 86 if (div) 87 cfg |= (2 * div - 1) & CFG_DIVIDER_MASK; 88 89 if (n_val) 90 cfg |= CFG_MODE_DUAL_EDGE; 91 92 writel(cfg, base + regs->cfg_rcgr); /* Write new clock configuration */ 93 94 /* Inform h/w to start using the new config. */ 95 clk_bcr_update(base + regs->cmd_rcgr); 96 } 97 98 static int msm_clk_probe(struct udevice *dev) 99 { 100 struct msm_clk_priv *priv = dev_get_priv(dev); 101 102 priv->base = devfdt_get_addr(dev); 103 if (priv->base == FDT_ADDR_T_NONE) 104 return -EINVAL; 105 106 return 0; 107 } 108 109 static ulong msm_clk_set_rate(struct clk *clk, ulong rate) 110 { 111 return msm_set_rate(clk, rate); 112 } 113 114 static struct clk_ops msm_clk_ops = { 115 .set_rate = msm_clk_set_rate, 116 }; 117 118 static const struct udevice_id msm_clk_ids[] = { 119 { .compatible = "qcom,gcc-msm8916" }, 120 { .compatible = "qcom,gcc-apq8016" }, 121 { .compatible = "qcom,gcc-msm8996" }, 122 { .compatible = "qcom,gcc-apq8096" }, 123 { } 124 }; 125 126 U_BOOT_DRIVER(clk_msm) = { 127 .name = "clk_msm", 128 .id = UCLASS_CLK, 129 .of_match = msm_clk_ids, 130 .ops = &msm_clk_ops, 131 .priv_auto_alloc_size = sizeof(struct msm_clk_priv), 132 .probe = msm_clk_probe, 133 }; 134