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