1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Samsung Exynos7420 clock driver. 4 * Copyright (C) 2016 Samsung Electronics 5 * Thomas Abraham <thomas.ab@samsung.com> 6 */ 7 8 #include <common.h> 9 #include <dm.h> 10 #include <errno.h> 11 #include <clk-uclass.h> 12 #include <asm/io.h> 13 #include <dt-bindings/clock/exynos7420-clk.h> 14 #include "clk-pll.h" 15 16 #define DIVIDER(reg, shift, mask) \ 17 (((readl(reg) >> shift) & mask) + 1) 18 19 /* CMU TOPC block device structure */ 20 struct exynos7420_clk_cmu_topc { 21 unsigned int rsvd1[68]; 22 unsigned int bus0_pll_con[2]; 23 unsigned int rsvd2[2]; 24 unsigned int bus1_pll_con[2]; 25 unsigned int rsvd3[54]; 26 unsigned int mux_sel[6]; 27 unsigned int rsvd4[250]; 28 unsigned int div[4]; 29 }; 30 31 /* CMU TOP0 block device structure */ 32 struct exynos7420_clk_cmu_top0 { 33 unsigned int rsvd0[128]; 34 unsigned int mux_sel[7]; 35 unsigned int rsvd1[261]; 36 unsigned int div_peric[5]; 37 }; 38 39 /** 40 * struct exynos7420_clk_topc_priv - private data for CMU topc clock driver. 41 * 42 * @topc: base address of the memory mapped CMU TOPC controller. 43 * @fin_freq: frequency of the Oscillator clock. 44 * @sclk_bus0_pll_a: frequency of sclk_bus0_pll_a clock. 45 * @sclk_bus1_pll_a: frequency of sclk_bus1_pll_a clock. 46 */ 47 struct exynos7420_clk_topc_priv { 48 struct exynos7420_clk_cmu_topc *topc; 49 unsigned long fin_freq; 50 unsigned long sclk_bus0_pll_a; 51 unsigned long sclk_bus1_pll_a; 52 }; 53 54 /** 55 * struct exynos7420_clk_top0_priv - private data for CMU top0 clock driver. 56 * 57 * @top0: base address of the memory mapped CMU TOP0 controller. 58 * @mout_top0_bus0_pll_half: frequency of mout_top0_bus0_pll_half clock 59 * @sclk_uart2: frequency of sclk_uart2 clock. 60 */ 61 struct exynos7420_clk_top0_priv { 62 struct exynos7420_clk_cmu_top0 *top0; 63 unsigned long mout_top0_bus0_pll_half; 64 unsigned long sclk_uart2; 65 }; 66 67 static ulong exynos7420_topc_get_rate(struct clk *clk) 68 { 69 struct exynos7420_clk_topc_priv *priv = dev_get_priv(clk->dev); 70 71 switch (clk->id) { 72 case DOUT_SCLK_BUS0_PLL: 73 case SCLK_BUS0_PLL_A: 74 case SCLK_BUS0_PLL_B: 75 return priv->sclk_bus0_pll_a; 76 case DOUT_SCLK_BUS1_PLL: 77 case SCLK_BUS1_PLL_A: 78 case SCLK_BUS1_PLL_B: 79 return priv->sclk_bus1_pll_a; 80 default: 81 return 0; 82 } 83 } 84 85 static struct clk_ops exynos7420_clk_topc_ops = { 86 .get_rate = exynos7420_topc_get_rate, 87 }; 88 89 static int exynos7420_clk_topc_probe(struct udevice *dev) 90 { 91 struct exynos7420_clk_topc_priv *priv = dev_get_priv(dev); 92 struct exynos7420_clk_cmu_topc *topc; 93 struct clk in_clk; 94 unsigned long rate; 95 fdt_addr_t base; 96 int ret; 97 98 base = devfdt_get_addr(dev); 99 if (base == FDT_ADDR_T_NONE) 100 return -EINVAL; 101 102 topc = (struct exynos7420_clk_cmu_topc *)base; 103 priv->topc = topc; 104 105 ret = clk_get_by_index(dev, 0, &in_clk); 106 if (ret >= 0) 107 priv->fin_freq = clk_get_rate(&in_clk); 108 109 rate = pll145x_get_rate(&topc->bus0_pll_con[0], priv->fin_freq); 110 if (readl(&topc->mux_sel[1]) & (1 << 16)) 111 rate >>= 1; 112 rate /= DIVIDER(&topc->div[3], 0, 0xf); 113 priv->sclk_bus0_pll_a = rate; 114 115 rate = pll145x_get_rate(&topc->bus1_pll_con[0], priv->fin_freq) / 116 DIVIDER(&topc->div[3], 8, 0xf); 117 priv->sclk_bus1_pll_a = rate; 118 119 return 0; 120 } 121 122 static ulong exynos7420_top0_get_rate(struct clk *clk) 123 { 124 struct exynos7420_clk_top0_priv *priv = dev_get_priv(clk->dev); 125 struct exynos7420_clk_cmu_top0 *top0 = priv->top0; 126 127 switch (clk->id) { 128 case CLK_SCLK_UART2: 129 return priv->mout_top0_bus0_pll_half / 130 DIVIDER(&top0->div_peric[3], 8, 0xf); 131 default: 132 return 0; 133 } 134 } 135 136 static struct clk_ops exynos7420_clk_top0_ops = { 137 .get_rate = exynos7420_top0_get_rate, 138 }; 139 140 static int exynos7420_clk_top0_probe(struct udevice *dev) 141 { 142 struct exynos7420_clk_top0_priv *priv; 143 struct exynos7420_clk_cmu_top0 *top0; 144 struct clk in_clk; 145 fdt_addr_t base; 146 int ret; 147 148 priv = dev_get_priv(dev); 149 if (!priv) 150 return -EINVAL; 151 152 base = devfdt_get_addr(dev); 153 if (base == FDT_ADDR_T_NONE) 154 return -EINVAL; 155 156 top0 = (struct exynos7420_clk_cmu_top0 *)base; 157 priv->top0 = top0; 158 159 ret = clk_get_by_index(dev, 1, &in_clk); 160 if (ret >= 0) { 161 priv->mout_top0_bus0_pll_half = 162 clk_get_rate(&in_clk); 163 if (readl(&top0->mux_sel[1]) & (1 << 16)) 164 priv->mout_top0_bus0_pll_half >>= 1; 165 } 166 167 return 0; 168 } 169 170 static ulong exynos7420_peric1_get_rate(struct clk *clk) 171 { 172 struct clk in_clk; 173 unsigned int ret; 174 unsigned long freq = 0; 175 176 switch (clk->id) { 177 case SCLK_UART2: 178 ret = clk_get_by_index(clk->dev, 3, &in_clk); 179 if (ret < 0) 180 return ret; 181 freq = clk_get_rate(&in_clk); 182 break; 183 } 184 185 return freq; 186 } 187 188 static struct clk_ops exynos7420_clk_peric1_ops = { 189 .get_rate = exynos7420_peric1_get_rate, 190 }; 191 192 static const struct udevice_id exynos7420_clk_topc_compat[] = { 193 { .compatible = "samsung,exynos7-clock-topc" }, 194 { } 195 }; 196 197 U_BOOT_DRIVER(exynos7420_clk_topc) = { 198 .name = "exynos7420-clock-topc", 199 .id = UCLASS_CLK, 200 .of_match = exynos7420_clk_topc_compat, 201 .probe = exynos7420_clk_topc_probe, 202 .priv_auto_alloc_size = sizeof(struct exynos7420_clk_topc_priv), 203 .ops = &exynos7420_clk_topc_ops, 204 .flags = DM_FLAG_PRE_RELOC, 205 }; 206 207 static const struct udevice_id exynos7420_clk_top0_compat[] = { 208 { .compatible = "samsung,exynos7-clock-top0" }, 209 { } 210 }; 211 212 U_BOOT_DRIVER(exynos7420_clk_top0) = { 213 .name = "exynos7420-clock-top0", 214 .id = UCLASS_CLK, 215 .of_match = exynos7420_clk_top0_compat, 216 .probe = exynos7420_clk_top0_probe, 217 .priv_auto_alloc_size = sizeof(struct exynos7420_clk_top0_priv), 218 .ops = &exynos7420_clk_top0_ops, 219 .flags = DM_FLAG_PRE_RELOC, 220 }; 221 222 static const struct udevice_id exynos7420_clk_peric1_compat[] = { 223 { .compatible = "samsung,exynos7-clock-peric1" }, 224 { } 225 }; 226 227 U_BOOT_DRIVER(exynos7420_clk_peric1) = { 228 .name = "exynos7420-clock-peric1", 229 .id = UCLASS_CLK, 230 .of_match = exynos7420_clk_peric1_compat, 231 .ops = &exynos7420_clk_peric1_ops, 232 .flags = DM_FLAG_PRE_RELOC, 233 }; 234