1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2018 - Beniamino Galvani <b.galvani@gmail.com> 4 * (C) Copyright 2018 - BayLibre, SAS 5 * Author: Neil Armstrong <narmstrong@baylibre.com> 6 */ 7 8 #include <common.h> 9 #include <asm/arch/clock-axg.h> 10 #include <asm/io.h> 11 #include <clk-uclass.h> 12 #include <dm.h> 13 #include <regmap.h> 14 #include <syscon.h> 15 #include <div64.h> 16 #include <dt-bindings/clock/axg-clkc.h> 17 #include "clk_meson.h" 18 19 #define XTAL_RATE 24000000 20 21 struct meson_clk { 22 struct regmap *map; 23 }; 24 25 static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id); 26 27 static struct meson_gate gates[] = { 28 /* Everything Else (EE) domain gates */ 29 MESON_GATE(CLKID_SPICC0, HHI_GCLK_MPEG0, 8), 30 MESON_GATE(CLKID_I2C, HHI_GCLK_MPEG0, 9), 31 MESON_GATE(CLKID_UART0, HHI_GCLK_MPEG0, 13), 32 MESON_GATE(CLKID_SPICC1, HHI_GCLK_MPEG0, 15), 33 MESON_GATE(CLKID_SD_EMMC_B, HHI_GCLK_MPEG0, 25), 34 MESON_GATE(CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26), 35 MESON_GATE(CLKID_ETH, HHI_GCLK_MPEG1, 3), 36 MESON_GATE(CLKID_UART1, HHI_GCLK_MPEG1, 16), 37 38 /* Always On (AO) domain gates */ 39 MESON_GATE(CLKID_AO_I2C, HHI_GCLK_AO, 4), 40 41 /* PLL Gates */ 42 /* CLKID_FCLK_DIV2 is critical for the SCPI Processor */ 43 MESON_GATE(CLKID_MPLL2, HHI_MPLL_CNTL9, 14), 44 /* CLKID_CLK81 is critical for the system */ 45 46 /* Peripheral Gates */ 47 MESON_GATE(CLKID_SD_EMMC_B_CLK0, HHI_SD_EMMC_CLK_CNTL, 23), 48 MESON_GATE(CLKID_SD_EMMC_C_CLK0, HHI_NAND_CLK_CNTL, 7), 49 }; 50 51 static int meson_set_gate(struct clk *clk, bool on) 52 { 53 struct meson_clk *priv = dev_get_priv(clk->dev); 54 struct meson_gate *gate; 55 56 if (clk->id >= ARRAY_SIZE(gates)) 57 return -ENOENT; 58 59 gate = &gates[clk->id]; 60 61 if (gate->reg == 0) 62 return 0; 63 64 regmap_update_bits(priv->map, gate->reg, 65 BIT(gate->bit), on ? BIT(gate->bit) : 0); 66 67 return 0; 68 } 69 70 static int meson_clk_enable(struct clk *clk) 71 { 72 return meson_set_gate(clk, true); 73 } 74 75 static int meson_clk_disable(struct clk *clk) 76 { 77 return meson_set_gate(clk, false); 78 } 79 80 static unsigned long meson_clk81_get_rate(struct clk *clk) 81 { 82 struct meson_clk *priv = dev_get_priv(clk->dev); 83 unsigned long parent_rate; 84 uint reg; 85 int parents[] = { 86 -1, 87 -1, 88 CLKID_FCLK_DIV7, 89 CLKID_MPLL1, 90 CLKID_MPLL2, 91 CLKID_FCLK_DIV4, 92 CLKID_FCLK_DIV3, 93 CLKID_FCLK_DIV5 94 }; 95 96 /* mux */ 97 regmap_read(priv->map, HHI_MPEG_CLK_CNTL, ®); 98 reg = (reg >> 12) & 7; 99 100 switch (reg) { 101 case 0: 102 parent_rate = XTAL_RATE; 103 break; 104 case 1: 105 return -ENOENT; 106 default: 107 parent_rate = meson_clk_get_rate_by_id(clk, parents[reg]); 108 } 109 110 /* divider */ 111 regmap_read(priv->map, HHI_MPEG_CLK_CNTL, ®); 112 reg = reg & ((1 << 7) - 1); 113 114 return parent_rate / reg; 115 } 116 117 static long mpll_rate_from_params(unsigned long parent_rate, 118 unsigned long sdm, 119 unsigned long n2) 120 { 121 unsigned long divisor = (SDM_DEN * n2) + sdm; 122 123 if (n2 < N2_MIN) 124 return -EINVAL; 125 126 return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor); 127 } 128 129 static struct parm meson_mpll0_parm[3] = { 130 {HHI_MPLL_CNTL7, 0, 14}, /* psdm */ 131 {HHI_MPLL_CNTL7, 16, 9}, /* pn2 */ 132 }; 133 134 static struct parm meson_mpll1_parm[3] = { 135 {HHI_MPLL_CNTL8, 0, 14}, /* psdm */ 136 {HHI_MPLL_CNTL8, 16, 9}, /* pn2 */ 137 }; 138 139 static struct parm meson_mpll2_parm[3] = { 140 {HHI_MPLL_CNTL9, 0, 14}, /* psdm */ 141 {HHI_MPLL_CNTL9, 16, 9}, /* pn2 */ 142 }; 143 144 /* 145 * MultiPhase Locked Loops are outputs from a PLL with additional frequency 146 * scaling capabilities. MPLL rates are calculated as: 147 * 148 * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384) 149 */ 150 static ulong meson_mpll_get_rate(struct clk *clk, unsigned long id) 151 { 152 struct meson_clk *priv = dev_get_priv(clk->dev); 153 struct parm *psdm, *pn2; 154 unsigned long sdm, n2; 155 unsigned long parent_rate; 156 uint reg; 157 158 switch (id) { 159 case CLKID_MPLL0: 160 psdm = &meson_mpll0_parm[0]; 161 pn2 = &meson_mpll0_parm[1]; 162 break; 163 case CLKID_MPLL1: 164 psdm = &meson_mpll1_parm[0]; 165 pn2 = &meson_mpll1_parm[1]; 166 break; 167 case CLKID_MPLL2: 168 psdm = &meson_mpll2_parm[0]; 169 pn2 = &meson_mpll2_parm[1]; 170 break; 171 default: 172 return -ENOENT; 173 } 174 175 parent_rate = meson_clk_get_rate_by_id(clk, CLKID_FIXED_PLL); 176 if (IS_ERR_VALUE(parent_rate)) 177 return parent_rate; 178 179 regmap_read(priv->map, psdm->reg_off, ®); 180 sdm = PARM_GET(psdm->width, psdm->shift, reg); 181 182 regmap_read(priv->map, pn2->reg_off, ®); 183 n2 = PARM_GET(pn2->width, pn2->shift, reg); 184 185 return mpll_rate_from_params(parent_rate, sdm, n2); 186 } 187 188 static struct parm meson_fixed_pll_parm[3] = { 189 {HHI_MPLL_CNTL, 0, 9}, /* pm */ 190 {HHI_MPLL_CNTL, 9, 5}, /* pn */ 191 {HHI_MPLL_CNTL, 16, 2}, /* pod */ 192 }; 193 194 static struct parm meson_sys_pll_parm[3] = { 195 {HHI_SYS_PLL_CNTL, 0, 9}, /* pm */ 196 {HHI_SYS_PLL_CNTL, 9, 5}, /* pn */ 197 {HHI_SYS_PLL_CNTL, 16, 2}, /* pod */ 198 }; 199 200 static ulong meson_pll_get_rate(struct clk *clk, unsigned long id) 201 { 202 struct meson_clk *priv = dev_get_priv(clk->dev); 203 struct parm *pm, *pn, *pod; 204 unsigned long parent_rate_mhz = XTAL_RATE / 1000000; 205 u16 n, m, od; 206 uint reg; 207 208 switch (id) { 209 case CLKID_FIXED_PLL: 210 pm = &meson_fixed_pll_parm[0]; 211 pn = &meson_fixed_pll_parm[1]; 212 pod = &meson_fixed_pll_parm[2]; 213 break; 214 case CLKID_SYS_PLL: 215 pm = &meson_sys_pll_parm[0]; 216 pn = &meson_sys_pll_parm[1]; 217 pod = &meson_sys_pll_parm[2]; 218 break; 219 default: 220 return -ENOENT; 221 } 222 223 regmap_read(priv->map, pn->reg_off, ®); 224 n = PARM_GET(pn->width, pn->shift, reg); 225 226 regmap_read(priv->map, pm->reg_off, ®); 227 m = PARM_GET(pm->width, pm->shift, reg); 228 229 regmap_read(priv->map, pod->reg_off, ®); 230 od = PARM_GET(pod->width, pod->shift, reg); 231 232 return ((parent_rate_mhz * m / n) >> od) * 1000000; 233 } 234 235 static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id) 236 { 237 ulong rate; 238 239 switch (id) { 240 case CLKID_FIXED_PLL: 241 case CLKID_SYS_PLL: 242 rate = meson_pll_get_rate(clk, id); 243 break; 244 case CLKID_FCLK_DIV2: 245 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 2; 246 break; 247 case CLKID_FCLK_DIV3: 248 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 3; 249 break; 250 case CLKID_FCLK_DIV4: 251 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 4; 252 break; 253 case CLKID_FCLK_DIV5: 254 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 5; 255 break; 256 case CLKID_FCLK_DIV7: 257 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 7; 258 break; 259 case CLKID_MPLL0: 260 case CLKID_MPLL1: 261 case CLKID_MPLL2: 262 rate = meson_mpll_get_rate(clk, id); 263 break; 264 case CLKID_CLK81: 265 rate = meson_clk81_get_rate(clk); 266 break; 267 default: 268 if (gates[id].reg != 0) { 269 /* a clock gate */ 270 rate = meson_clk81_get_rate(clk); 271 break; 272 } 273 return -ENOENT; 274 } 275 276 debug("clock %lu has rate %lu\n", id, rate); 277 return rate; 278 } 279 280 static ulong meson_clk_get_rate(struct clk *clk) 281 { 282 return meson_clk_get_rate_by_id(clk, clk->id); 283 } 284 285 static int meson_clk_probe(struct udevice *dev) 286 { 287 struct meson_clk *priv = dev_get_priv(dev); 288 289 priv->map = syscon_node_to_regmap(dev_get_parent(dev)->node); 290 if (IS_ERR(priv->map)) 291 return PTR_ERR(priv->map); 292 293 debug("meson-clk-axg: probed\n"); 294 295 return 0; 296 } 297 298 static struct clk_ops meson_clk_ops = { 299 .disable = meson_clk_disable, 300 .enable = meson_clk_enable, 301 .get_rate = meson_clk_get_rate, 302 }; 303 304 static const struct udevice_id meson_clk_ids[] = { 305 { .compatible = "amlogic,axg-clkc" }, 306 { } 307 }; 308 309 U_BOOT_DRIVER(meson_clk_axg) = { 310 .name = "meson_clk_axg", 311 .id = UCLASS_CLK, 312 .of_match = meson_clk_ids, 313 .priv_auto_alloc_size = sizeof(struct meson_clk), 314 .ops = &meson_clk_ops, 315 .probe = meson_clk_probe, 316 }; 317