1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2015 Purna Chandra Mandal <purna.mandal@microchip.com> 4 * 5 */ 6 7 #include <common.h> 8 #include <clk-uclass.h> 9 #include <dm.h> 10 #include <div64.h> 11 #include <wait_bit.h> 12 #include <dm/lists.h> 13 #include <asm/io.h> 14 #include <mach/pic32.h> 15 #include <dt-bindings/clock/microchip,clock.h> 16 17 DECLARE_GLOBAL_DATA_PTR; 18 19 /* Primary oscillator */ 20 #define SYS_POSC_CLK_HZ 24000000 21 22 /* FRC clk rate */ 23 #define SYS_FRC_CLK_HZ 8000000 24 25 /* Clock Registers */ 26 #define OSCCON 0x0000 27 #define OSCTUNE 0x0010 28 #define SPLLCON 0x0020 29 #define REFO1CON 0x0080 30 #define REFO1TRIM 0x0090 31 #define PB1DIV 0x0140 32 33 /* SPLL */ 34 #define ICLK_MASK 0x00000080 35 #define PLLIDIV_MASK 0x00000007 36 #define PLLODIV_MASK 0x00000007 37 #define CUROSC_MASK 0x00000007 38 #define PLLMUL_MASK 0x0000007F 39 #define FRCDIV_MASK 0x00000007 40 41 /* PBCLK */ 42 #define PBDIV_MASK 0x00000007 43 44 /* SYSCLK MUX */ 45 #define SCLK_SRC_FRC1 0 46 #define SCLK_SRC_SPLL 1 47 #define SCLK_SRC_POSC 2 48 #define SCLK_SRC_FRC2 7 49 50 /* Reference Oscillator Control Reg fields */ 51 #define REFO_SEL_MASK 0x0f 52 #define REFO_SEL_SHIFT 0 53 #define REFO_ACTIVE BIT(8) 54 #define REFO_DIVSW_EN BIT(9) 55 #define REFO_OE BIT(12) 56 #define REFO_ON BIT(15) 57 #define REFO_DIV_SHIFT 16 58 #define REFO_DIV_MASK 0x7fff 59 60 /* Reference Oscillator Trim Register Fields */ 61 #define REFO_TRIM_REG 0x10 62 #define REFO_TRIM_MASK 0x1ff 63 #define REFO_TRIM_SHIFT 23 64 #define REFO_TRIM_MAX 511 65 66 #define ROCLK_SRC_SCLK 0x0 67 #define ROCLK_SRC_SPLL 0x7 68 #define ROCLK_SRC_ROCLKI 0x8 69 70 /* Memory PLL */ 71 #define MPLL_IDIV 0x3f 72 #define MPLL_MULT 0xff 73 #define MPLL_ODIV1 0x7 74 #define MPLL_ODIV2 0x7 75 #define MPLL_VREG_RDY BIT(23) 76 #define MPLL_RDY BIT(31) 77 #define MPLL_IDIV_SHIFT 0 78 #define MPLL_MULT_SHIFT 8 79 #define MPLL_ODIV1_SHIFT 24 80 #define MPLL_ODIV2_SHIFT 27 81 #define MPLL_IDIV_INIT 0x03 82 #define MPLL_MULT_INIT 0x32 83 #define MPLL_ODIV1_INIT 0x02 84 #define MPLL_ODIV2_INIT 0x01 85 86 struct pic32_clk_priv { 87 void __iomem *iobase; 88 void __iomem *syscfg_base; 89 }; 90 91 static ulong pic32_get_pll_rate(struct pic32_clk_priv *priv) 92 { 93 u32 iclk, idiv, odiv, mult; 94 ulong plliclk, v; 95 96 v = readl(priv->iobase + SPLLCON); 97 iclk = (v & ICLK_MASK); 98 idiv = ((v >> 8) & PLLIDIV_MASK) + 1; 99 odiv = ((v >> 24) & PLLODIV_MASK); 100 mult = ((v >> 16) & PLLMUL_MASK) + 1; 101 102 plliclk = iclk ? SYS_FRC_CLK_HZ : SYS_POSC_CLK_HZ; 103 104 if (odiv < 2) 105 odiv = 2; 106 else if (odiv < 5) 107 odiv = (1 << odiv); 108 else 109 odiv = 32; 110 111 return ((plliclk / idiv) * mult) / odiv; 112 } 113 114 static ulong pic32_get_sysclk(struct pic32_clk_priv *priv) 115 { 116 ulong v; 117 ulong hz; 118 ulong div, frcdiv; 119 ulong curr_osc; 120 121 /* get clk source */ 122 v = readl(priv->iobase + OSCCON); 123 curr_osc = (v >> 12) & CUROSC_MASK; 124 switch (curr_osc) { 125 case SCLK_SRC_FRC1: 126 case SCLK_SRC_FRC2: 127 frcdiv = ((v >> 24) & FRCDIV_MASK); 128 div = ((1 << frcdiv) + 1) + (128 * (frcdiv == 7)); 129 hz = SYS_FRC_CLK_HZ / div; 130 break; 131 132 case SCLK_SRC_SPLL: 133 hz = pic32_get_pll_rate(priv); 134 break; 135 136 case SCLK_SRC_POSC: 137 hz = SYS_POSC_CLK_HZ; 138 break; 139 140 default: 141 hz = 0; 142 printf("clk: unknown sclk_src.\n"); 143 break; 144 } 145 146 return hz; 147 } 148 149 static ulong pic32_get_pbclk(struct pic32_clk_priv *priv, int periph) 150 { 151 void __iomem *reg; 152 ulong div, clk_freq; 153 154 WARN_ON((periph < PB1CLK) || (periph > PB7CLK)); 155 156 clk_freq = pic32_get_sysclk(priv); 157 158 reg = priv->iobase + PB1DIV + (periph - PB1CLK) * 0x10; 159 div = (readl(reg) & PBDIV_MASK) + 1; 160 161 return clk_freq / div; 162 } 163 164 static ulong pic32_get_cpuclk(struct pic32_clk_priv *priv) 165 { 166 return pic32_get_pbclk(priv, PB7CLK); 167 } 168 169 static ulong pic32_set_refclk(struct pic32_clk_priv *priv, int periph, 170 int parent_rate, int rate, int parent_id) 171 { 172 void __iomem *reg; 173 u32 div, trim, v; 174 u64 frac; 175 176 WARN_ON((periph < REF1CLK) || (periph > REF5CLK)); 177 178 /* calculate dividers, 179 * rate = parent_rate / [2 * (div + (trim / 512))] 180 */ 181 if (parent_rate <= rate) { 182 div = 0; 183 trim = 0; 184 } else { 185 div = parent_rate / (rate << 1); 186 frac = parent_rate; 187 frac <<= 8; 188 do_div(frac, rate); 189 frac -= (u64)(div << 9); 190 trim = (frac >= REFO_TRIM_MAX) ? REFO_TRIM_MAX : (u32)frac; 191 } 192 193 reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20; 194 195 /* disable clk */ 196 writel(REFO_ON | REFO_OE, reg + _CLR_OFFSET); 197 198 /* wait till previous src change is active */ 199 wait_for_bit_le32(reg, REFO_DIVSW_EN | REFO_ACTIVE, 200 false, CONFIG_SYS_HZ, false); 201 202 /* parent_id */ 203 v = readl(reg); 204 v &= ~(REFO_SEL_MASK << REFO_SEL_SHIFT); 205 v |= (parent_id << REFO_SEL_SHIFT); 206 207 /* apply rodiv */ 208 v &= ~(REFO_DIV_MASK << REFO_DIV_SHIFT); 209 v |= (div << REFO_DIV_SHIFT); 210 writel(v, reg); 211 212 /* apply trim */ 213 v = readl(reg + REFO_TRIM_REG); 214 v &= ~(REFO_TRIM_MASK << REFO_TRIM_SHIFT); 215 v |= (trim << REFO_TRIM_SHIFT); 216 writel(v, reg + REFO_TRIM_REG); 217 218 /* enable clk */ 219 writel(REFO_ON | REFO_OE, reg + _SET_OFFSET); 220 221 /* switch divider */ 222 writel(REFO_DIVSW_EN, reg + _SET_OFFSET); 223 224 /* wait for divider switching to complete */ 225 return wait_for_bit_le32(reg, REFO_DIVSW_EN, false, 226 CONFIG_SYS_HZ, false); 227 } 228 229 static ulong pic32_get_refclk(struct pic32_clk_priv *priv, int periph) 230 { 231 u32 rodiv, rotrim, rosel, v, parent_rate; 232 void __iomem *reg; 233 u64 rate64; 234 235 WARN_ON((periph < REF1CLK) || (periph > REF5CLK)); 236 237 reg = priv->iobase + REFO1CON + (periph - REF1CLK) * 0x20; 238 v = readl(reg); 239 /* get rosel */ 240 rosel = (v >> REFO_SEL_SHIFT) & REFO_SEL_MASK; 241 /* get div */ 242 rodiv = (v >> REFO_DIV_SHIFT) & REFO_DIV_MASK; 243 244 /* get trim */ 245 v = readl(reg + REFO_TRIM_REG); 246 rotrim = (v >> REFO_TRIM_SHIFT) & REFO_TRIM_MASK; 247 248 if (!rodiv) 249 return 0; 250 251 /* get parent rate */ 252 switch (rosel) { 253 case ROCLK_SRC_SCLK: 254 parent_rate = pic32_get_cpuclk(priv); 255 break; 256 case ROCLK_SRC_SPLL: 257 parent_rate = pic32_get_pll_rate(priv); 258 break; 259 default: 260 parent_rate = 0; 261 break; 262 } 263 264 /* Calculation 265 * rate = parent_rate / [2 * (div + (trim / 512))] 266 */ 267 if (rotrim) { 268 rodiv <<= 9; 269 rodiv += rotrim; 270 rate64 = parent_rate; 271 rate64 <<= 8; 272 do_div(rate64, rodiv); 273 v = (u32)rate64; 274 } else { 275 v = parent_rate / (rodiv << 1); 276 } 277 return v; 278 } 279 280 static ulong pic32_get_mpll_rate(struct pic32_clk_priv *priv) 281 { 282 u32 v, idiv, mul; 283 u32 odiv1, odiv2; 284 u64 rate; 285 286 v = readl(priv->syscfg_base + CFGMPLL); 287 idiv = v & MPLL_IDIV; 288 mul = (v >> MPLL_MULT_SHIFT) & MPLL_MULT; 289 odiv1 = (v >> MPLL_ODIV1_SHIFT) & MPLL_ODIV1; 290 odiv2 = (v >> MPLL_ODIV2_SHIFT) & MPLL_ODIV2; 291 292 rate = (SYS_POSC_CLK_HZ / idiv) * mul; 293 do_div(rate, odiv1); 294 do_div(rate, odiv2); 295 296 return (ulong)rate; 297 } 298 299 static int pic32_mpll_init(struct pic32_clk_priv *priv) 300 { 301 u32 v, mask; 302 303 /* initialize */ 304 v = (MPLL_IDIV_INIT << MPLL_IDIV_SHIFT) | 305 (MPLL_MULT_INIT << MPLL_MULT_SHIFT) | 306 (MPLL_ODIV1_INIT << MPLL_ODIV1_SHIFT) | 307 (MPLL_ODIV2_INIT << MPLL_ODIV2_SHIFT); 308 309 writel(v, priv->syscfg_base + CFGMPLL); 310 311 /* Wait for ready */ 312 mask = MPLL_RDY | MPLL_VREG_RDY; 313 return wait_for_bit_le32(priv->syscfg_base + CFGMPLL, mask, 314 true, get_tbclk(), false); 315 } 316 317 static void pic32_clk_init(struct udevice *dev) 318 { 319 const void *blob = gd->fdt_blob; 320 struct pic32_clk_priv *priv; 321 ulong rate, pll_hz; 322 char propname[50]; 323 int i; 324 325 priv = dev_get_priv(dev); 326 pll_hz = pic32_get_pll_rate(priv); 327 328 /* Initialize REFOs as not initialized and enabled on reset. */ 329 for (i = REF1CLK; i <= REF5CLK; i++) { 330 snprintf(propname, sizeof(propname), 331 "microchip,refo%d-frequency", i - REF1CLK + 1); 332 rate = fdtdec_get_int(blob, dev_of_offset(dev), propname, 0); 333 if (rate) 334 pic32_set_refclk(priv, i, pll_hz, rate, ROCLK_SRC_SPLL); 335 } 336 337 /* Memory PLL */ 338 pic32_mpll_init(priv); 339 } 340 341 static ulong pic32_get_rate(struct clk *clk) 342 { 343 struct pic32_clk_priv *priv = dev_get_priv(clk->dev); 344 ulong rate; 345 346 switch (clk->id) { 347 case PB1CLK ... PB7CLK: 348 rate = pic32_get_pbclk(priv, clk->id); 349 break; 350 case REF1CLK ... REF5CLK: 351 rate = pic32_get_refclk(priv, clk->id); 352 break; 353 case PLLCLK: 354 rate = pic32_get_pll_rate(priv); 355 break; 356 case MPLL: 357 rate = pic32_get_mpll_rate(priv); 358 break; 359 default: 360 rate = 0; 361 break; 362 } 363 364 return rate; 365 } 366 367 static ulong pic32_set_rate(struct clk *clk, ulong rate) 368 { 369 struct pic32_clk_priv *priv = dev_get_priv(clk->dev); 370 ulong pll_hz; 371 372 switch (clk->id) { 373 case REF1CLK ... REF5CLK: 374 pll_hz = pic32_get_pll_rate(priv); 375 pic32_set_refclk(priv, clk->id, pll_hz, rate, ROCLK_SRC_SPLL); 376 break; 377 default: 378 break; 379 } 380 381 return rate; 382 } 383 384 static struct clk_ops pic32_pic32_clk_ops = { 385 .set_rate = pic32_set_rate, 386 .get_rate = pic32_get_rate, 387 }; 388 389 static int pic32_clk_probe(struct udevice *dev) 390 { 391 struct pic32_clk_priv *priv = dev_get_priv(dev); 392 fdt_addr_t addr; 393 fdt_size_t size; 394 395 addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg", 396 &size); 397 if (addr == FDT_ADDR_T_NONE) 398 return -EINVAL; 399 400 priv->iobase = ioremap(addr, size); 401 if (!priv->iobase) 402 return -EINVAL; 403 404 priv->syscfg_base = pic32_get_syscfg_base(); 405 406 /* initialize clocks */ 407 pic32_clk_init(dev); 408 409 return 0; 410 } 411 412 static const struct udevice_id pic32_clk_ids[] = { 413 { .compatible = "microchip,pic32mzda-clk"}, 414 {} 415 }; 416 417 U_BOOT_DRIVER(pic32_clk) = { 418 .name = "pic32_clk", 419 .id = UCLASS_CLK, 420 .of_match = pic32_clk_ids, 421 .ops = &pic32_pic32_clk_ops, 422 .probe = pic32_clk_probe, 423 .priv_auto_alloc_size = sizeof(struct pic32_clk_priv), 424 }; 425