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