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