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