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