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