1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * JZ4770 SoC CGU driver 4 * Copyright 2018, Paul Cercueil <paul@crapouillou.net> 5 */ 6 7 #include <linux/bitops.h> 8 #include <linux/clk-provider.h> 9 #include <linux/delay.h> 10 #include <linux/io.h> 11 #include <linux/of.h> 12 #include <linux/syscore_ops.h> 13 #include <dt-bindings/clock/jz4770-cgu.h> 14 #include "cgu.h" 15 16 /* 17 * CPM registers offset address definition 18 */ 19 #define CGU_REG_CPCCR 0x00 20 #define CGU_REG_LCR 0x04 21 #define CGU_REG_CPPCR0 0x10 22 #define CGU_REG_CLKGR0 0x20 23 #define CGU_REG_OPCR 0x24 24 #define CGU_REG_CLKGR1 0x28 25 #define CGU_REG_CPPCR1 0x30 26 #define CGU_REG_USBPCR1 0x48 27 #define CGU_REG_USBCDR 0x50 28 #define CGU_REG_I2SCDR 0x60 29 #define CGU_REG_LPCDR 0x64 30 #define CGU_REG_MSC0CDR 0x68 31 #define CGU_REG_UHCCDR 0x6c 32 #define CGU_REG_SSICDR 0x74 33 #define CGU_REG_CIMCDR 0x7c 34 #define CGU_REG_GPSCDR 0x80 35 #define CGU_REG_PCMCDR 0x84 36 #define CGU_REG_GPUCDR 0x88 37 #define CGU_REG_MSC1CDR 0xA4 38 #define CGU_REG_MSC2CDR 0xA8 39 #define CGU_REG_BCHCDR 0xAC 40 41 /* bits within the LCR register */ 42 #define LCR_LPM BIT(0) /* Low Power Mode */ 43 44 /* bits within the OPCR register */ 45 #define OPCR_SPENDH BIT(5) /* UHC PHY suspend */ 46 47 /* bits within the USBPCR1 register */ 48 #define USBPCR1_UHC_POWER BIT(5) /* UHC PHY power down */ 49 50 static struct ingenic_cgu *cgu; 51 52 static int jz4770_uhc_phy_enable(struct clk_hw *hw) 53 { 54 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR; 55 void __iomem *reg_usbpcr1 = cgu->base + CGU_REG_USBPCR1; 56 57 writel(readl(reg_opcr) & ~OPCR_SPENDH, reg_opcr); 58 writel(readl(reg_usbpcr1) | USBPCR1_UHC_POWER, reg_usbpcr1); 59 return 0; 60 } 61 62 static void jz4770_uhc_phy_disable(struct clk_hw *hw) 63 { 64 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR; 65 void __iomem *reg_usbpcr1 = cgu->base + CGU_REG_USBPCR1; 66 67 writel(readl(reg_usbpcr1) & ~USBPCR1_UHC_POWER, reg_usbpcr1); 68 writel(readl(reg_opcr) | OPCR_SPENDH, reg_opcr); 69 } 70 71 static int jz4770_uhc_phy_is_enabled(struct clk_hw *hw) 72 { 73 void __iomem *reg_opcr = cgu->base + CGU_REG_OPCR; 74 void __iomem *reg_usbpcr1 = cgu->base + CGU_REG_USBPCR1; 75 76 return !(readl(reg_opcr) & OPCR_SPENDH) && 77 (readl(reg_usbpcr1) & USBPCR1_UHC_POWER); 78 } 79 80 static const struct clk_ops jz4770_uhc_phy_ops = { 81 .enable = jz4770_uhc_phy_enable, 82 .disable = jz4770_uhc_phy_disable, 83 .is_enabled = jz4770_uhc_phy_is_enabled, 84 }; 85 86 static const s8 pll_od_encoding[8] = { 87 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3, 88 }; 89 90 static const u8 jz4770_cgu_cpccr_div_table[] = { 91 1, 2, 3, 4, 6, 8, 12, 92 }; 93 94 static const struct ingenic_cgu_clk_info jz4770_cgu_clocks[] = { 95 96 /* External clocks */ 97 98 [JZ4770_CLK_EXT] = { "ext", CGU_CLK_EXT }, 99 [JZ4770_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT }, 100 101 /* PLLs */ 102 103 [JZ4770_CLK_PLL0] = { 104 "pll0", CGU_CLK_PLL, 105 .parents = { JZ4770_CLK_EXT }, 106 .pll = { 107 .reg = CGU_REG_CPPCR0, 108 .m_shift = 24, 109 .m_bits = 7, 110 .m_offset = 1, 111 .n_shift = 18, 112 .n_bits = 5, 113 .n_offset = 1, 114 .od_shift = 16, 115 .od_bits = 2, 116 .od_max = 8, 117 .od_encoding = pll_od_encoding, 118 .bypass_bit = 9, 119 .enable_bit = 8, 120 .stable_bit = 10, 121 }, 122 }, 123 124 [JZ4770_CLK_PLL1] = { 125 /* TODO: PLL1 can depend on PLL0 */ 126 "pll1", CGU_CLK_PLL, 127 .parents = { JZ4770_CLK_EXT }, 128 .pll = { 129 .reg = CGU_REG_CPPCR1, 130 .m_shift = 24, 131 .m_bits = 7, 132 .m_offset = 1, 133 .n_shift = 18, 134 .n_bits = 5, 135 .n_offset = 1, 136 .od_shift = 16, 137 .od_bits = 2, 138 .od_max = 8, 139 .od_encoding = pll_od_encoding, 140 .enable_bit = 7, 141 .stable_bit = 6, 142 .no_bypass_bit = true, 143 }, 144 }, 145 146 /* Main clocks */ 147 148 [JZ4770_CLK_CCLK] = { 149 "cclk", CGU_CLK_DIV, 150 .parents = { JZ4770_CLK_PLL0, }, 151 .div = { 152 CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 153 jz4770_cgu_cpccr_div_table, 154 }, 155 }, 156 [JZ4770_CLK_H0CLK] = { 157 "h0clk", CGU_CLK_DIV, 158 .parents = { JZ4770_CLK_PLL0, }, 159 .div = { 160 CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 161 jz4770_cgu_cpccr_div_table, 162 }, 163 }, 164 [JZ4770_CLK_H1CLK] = { 165 "h1clk", CGU_CLK_DIV | CGU_CLK_GATE, 166 .parents = { JZ4770_CLK_PLL0, }, 167 .div = { 168 CGU_REG_CPCCR, 24, 1, 4, 22, -1, -1, 169 jz4770_cgu_cpccr_div_table, 170 }, 171 .gate = { CGU_REG_CLKGR1, 7 }, 172 }, 173 [JZ4770_CLK_H2CLK] = { 174 "h2clk", CGU_CLK_DIV, 175 .parents = { JZ4770_CLK_PLL0, }, 176 .div = { 177 CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 178 jz4770_cgu_cpccr_div_table, 179 }, 180 }, 181 [JZ4770_CLK_C1CLK] = { 182 "c1clk", CGU_CLK_DIV | CGU_CLK_GATE, 183 .parents = { JZ4770_CLK_PLL0, }, 184 .div = { 185 CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 186 jz4770_cgu_cpccr_div_table, 187 }, 188 .gate = { CGU_REG_OPCR, 31, true }, // disable CCLK stop on idle 189 }, 190 [JZ4770_CLK_PCLK] = { 191 "pclk", CGU_CLK_DIV, 192 .parents = { JZ4770_CLK_PLL0, }, 193 .div = { 194 CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 195 jz4770_cgu_cpccr_div_table, 196 }, 197 }, 198 199 /* Those divided clocks can connect to PLL0 or PLL1 */ 200 201 [JZ4770_CLK_MMC0_MUX] = { 202 "mmc0_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 203 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 204 .mux = { CGU_REG_MSC0CDR, 30, 1 }, 205 .div = { CGU_REG_MSC0CDR, 0, 1, 7, -1, -1, 31 }, 206 .gate = { CGU_REG_MSC0CDR, 31 }, 207 }, 208 [JZ4770_CLK_MMC1_MUX] = { 209 "mmc1_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 210 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 211 .mux = { CGU_REG_MSC1CDR, 30, 1 }, 212 .div = { CGU_REG_MSC1CDR, 0, 1, 7, -1, -1, 31 }, 213 .gate = { CGU_REG_MSC1CDR, 31 }, 214 }, 215 [JZ4770_CLK_MMC2_MUX] = { 216 "mmc2_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 217 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 218 .mux = { CGU_REG_MSC2CDR, 30, 1 }, 219 .div = { CGU_REG_MSC2CDR, 0, 1, 7, -1, -1, 31 }, 220 .gate = { CGU_REG_MSC2CDR, 31 }, 221 }, 222 [JZ4770_CLK_CIM] = { 223 "cim", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 224 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 225 .mux = { CGU_REG_CIMCDR, 31, 1 }, 226 .div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 }, 227 .gate = { CGU_REG_CLKGR0, 26 }, 228 }, 229 [JZ4770_CLK_UHC] = { 230 "uhc", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 231 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 232 .mux = { CGU_REG_UHCCDR, 29, 1 }, 233 .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 }, 234 .gate = { CGU_REG_CLKGR0, 24 }, 235 }, 236 [JZ4770_CLK_GPU] = { 237 "gpu", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 238 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, -1 }, 239 .mux = { CGU_REG_GPUCDR, 31, 1 }, 240 .div = { CGU_REG_GPUCDR, 0, 1, 3, -1, -1, -1 }, 241 .gate = { CGU_REG_CLKGR1, 9 }, 242 }, 243 [JZ4770_CLK_BCH] = { 244 "bch", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 245 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 246 .mux = { CGU_REG_BCHCDR, 31, 1 }, 247 .div = { CGU_REG_BCHCDR, 0, 1, 3, -1, -1, -1 }, 248 .gate = { CGU_REG_CLKGR0, 1 }, 249 }, 250 [JZ4770_CLK_LPCLK_MUX] = { 251 "lpclk", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 252 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 253 .mux = { CGU_REG_LPCDR, 29, 1 }, 254 .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 }, 255 .gate = { CGU_REG_CLKGR0, 28 }, 256 }, 257 [JZ4770_CLK_GPS] = { 258 "gps", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 259 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, }, 260 .mux = { CGU_REG_GPSCDR, 31, 1 }, 261 .div = { CGU_REG_GPSCDR, 0, 1, 4, -1, -1, -1 }, 262 .gate = { CGU_REG_CLKGR0, 22 }, 263 }, 264 265 /* Those divided clocks can connect to EXT, PLL0 or PLL1 */ 266 267 [JZ4770_CLK_SSI_MUX] = { 268 "ssi_mux", CGU_CLK_DIV | CGU_CLK_MUX, 269 .parents = { JZ4770_CLK_EXT, -1, 270 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 }, 271 .mux = { CGU_REG_SSICDR, 30, 2 }, 272 .div = { CGU_REG_SSICDR, 0, 1, 6, -1, -1, -1 }, 273 }, 274 [JZ4770_CLK_PCM_MUX] = { 275 "pcm_mux", CGU_CLK_DIV | CGU_CLK_MUX, 276 .parents = { JZ4770_CLK_EXT, -1, 277 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 }, 278 .mux = { CGU_REG_PCMCDR, 30, 2 }, 279 .div = { CGU_REG_PCMCDR, 0, 1, 9, -1, -1, -1 }, 280 }, 281 [JZ4770_CLK_I2S] = { 282 "i2s", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 283 .parents = { JZ4770_CLK_EXT, -1, 284 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 }, 285 .mux = { CGU_REG_I2SCDR, 30, 2 }, 286 .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 }, 287 .gate = { CGU_REG_CLKGR1, 13 }, 288 }, 289 [JZ4770_CLK_OTG] = { 290 "usb", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX, 291 .parents = { JZ4770_CLK_EXT, -1, 292 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 }, 293 .mux = { CGU_REG_USBCDR, 30, 2 }, 294 .div = { CGU_REG_USBCDR, 0, 1, 8, -1, -1, -1 }, 295 .gate = { CGU_REG_CLKGR0, 2 }, 296 }, 297 298 /* Gate-only clocks */ 299 300 [JZ4770_CLK_SSI0] = { 301 "ssi0", CGU_CLK_GATE, 302 .parents = { JZ4770_CLK_SSI_MUX, }, 303 .gate = { CGU_REG_CLKGR0, 4 }, 304 }, 305 [JZ4770_CLK_SSI1] = { 306 "ssi1", CGU_CLK_GATE, 307 .parents = { JZ4770_CLK_SSI_MUX, }, 308 .gate = { CGU_REG_CLKGR0, 19 }, 309 }, 310 [JZ4770_CLK_SSI2] = { 311 "ssi2", CGU_CLK_GATE, 312 .parents = { JZ4770_CLK_SSI_MUX, }, 313 .gate = { CGU_REG_CLKGR0, 20 }, 314 }, 315 [JZ4770_CLK_PCM0] = { 316 "pcm0", CGU_CLK_GATE, 317 .parents = { JZ4770_CLK_PCM_MUX, }, 318 .gate = { CGU_REG_CLKGR1, 8 }, 319 }, 320 [JZ4770_CLK_PCM1] = { 321 "pcm1", CGU_CLK_GATE, 322 .parents = { JZ4770_CLK_PCM_MUX, }, 323 .gate = { CGU_REG_CLKGR1, 10 }, 324 }, 325 [JZ4770_CLK_DMA] = { 326 "dma", CGU_CLK_GATE, 327 .parents = { JZ4770_CLK_H2CLK, }, 328 .gate = { CGU_REG_CLKGR0, 21 }, 329 }, 330 [JZ4770_CLK_I2C0] = { 331 "i2c0", CGU_CLK_GATE, 332 .parents = { JZ4770_CLK_EXT, }, 333 .gate = { CGU_REG_CLKGR0, 5 }, 334 }, 335 [JZ4770_CLK_I2C1] = { 336 "i2c1", CGU_CLK_GATE, 337 .parents = { JZ4770_CLK_EXT, }, 338 .gate = { CGU_REG_CLKGR0, 6 }, 339 }, 340 [JZ4770_CLK_I2C2] = { 341 "i2c2", CGU_CLK_GATE, 342 .parents = { JZ4770_CLK_EXT, }, 343 .gate = { CGU_REG_CLKGR1, 15 }, 344 }, 345 [JZ4770_CLK_UART0] = { 346 "uart0", CGU_CLK_GATE, 347 .parents = { JZ4770_CLK_EXT, }, 348 .gate = { CGU_REG_CLKGR0, 15 }, 349 }, 350 [JZ4770_CLK_UART1] = { 351 "uart1", CGU_CLK_GATE, 352 .parents = { JZ4770_CLK_EXT, }, 353 .gate = { CGU_REG_CLKGR0, 16 }, 354 }, 355 [JZ4770_CLK_UART2] = { 356 "uart2", CGU_CLK_GATE, 357 .parents = { JZ4770_CLK_EXT, }, 358 .gate = { CGU_REG_CLKGR0, 17 }, 359 }, 360 [JZ4770_CLK_UART3] = { 361 "uart3", CGU_CLK_GATE, 362 .parents = { JZ4770_CLK_EXT, }, 363 .gate = { CGU_REG_CLKGR0, 18 }, 364 }, 365 [JZ4770_CLK_IPU] = { 366 "ipu", CGU_CLK_GATE, 367 .parents = { JZ4770_CLK_H0CLK, }, 368 .gate = { CGU_REG_CLKGR0, 29 }, 369 }, 370 [JZ4770_CLK_ADC] = { 371 "adc", CGU_CLK_GATE, 372 .parents = { JZ4770_CLK_EXT, }, 373 .gate = { CGU_REG_CLKGR0, 14 }, 374 }, 375 [JZ4770_CLK_AIC] = { 376 "aic", CGU_CLK_GATE, 377 .parents = { JZ4770_CLK_EXT, }, 378 .gate = { CGU_REG_CLKGR0, 8 }, 379 }, 380 [JZ4770_CLK_AUX] = { 381 "aux", CGU_CLK_GATE, 382 .parents = { JZ4770_CLK_C1CLK, }, 383 .gate = { CGU_REG_CLKGR1, 14 }, 384 }, 385 [JZ4770_CLK_VPU] = { 386 "vpu", CGU_CLK_GATE, 387 .parents = { JZ4770_CLK_H1CLK, }, 388 .gate = { CGU_REG_LCR, 30, false, 150 }, 389 }, 390 [JZ4770_CLK_MMC0] = { 391 "mmc0", CGU_CLK_GATE, 392 .parents = { JZ4770_CLK_MMC0_MUX, }, 393 .gate = { CGU_REG_CLKGR0, 3 }, 394 }, 395 [JZ4770_CLK_MMC1] = { 396 "mmc1", CGU_CLK_GATE, 397 .parents = { JZ4770_CLK_MMC1_MUX, }, 398 .gate = { CGU_REG_CLKGR0, 11 }, 399 }, 400 [JZ4770_CLK_MMC2] = { 401 "mmc2", CGU_CLK_GATE, 402 .parents = { JZ4770_CLK_MMC2_MUX, }, 403 .gate = { CGU_REG_CLKGR0, 12 }, 404 }, 405 [JZ4770_CLK_OTG_PHY] = { 406 "usb_phy", CGU_CLK_GATE, 407 .parents = { JZ4770_CLK_OTG }, 408 .gate = { CGU_REG_OPCR, 7, true, 50 }, 409 }, 410 411 /* Custom clocks */ 412 413 [JZ4770_CLK_UHC_PHY] = { 414 "uhc_phy", CGU_CLK_CUSTOM, 415 .parents = { JZ4770_CLK_UHC, -1, -1, -1 }, 416 .custom = { &jz4770_uhc_phy_ops }, 417 }, 418 419 [JZ4770_CLK_EXT512] = { 420 "ext/512", CGU_CLK_FIXDIV, 421 .parents = { JZ4770_CLK_EXT }, 422 .fixdiv = { 512 }, 423 }, 424 425 [JZ4770_CLK_RTC] = { 426 "rtc", CGU_CLK_MUX, 427 .parents = { JZ4770_CLK_EXT512, JZ4770_CLK_OSC32K, }, 428 .mux = { CGU_REG_OPCR, 2, 1}, 429 }, 430 }; 431 432 #if IS_ENABLED(CONFIG_PM_SLEEP) 433 static int jz4770_cgu_pm_suspend(void) 434 { 435 u32 val; 436 437 val = readl(cgu->base + CGU_REG_LCR); 438 writel(val | LCR_LPM, cgu->base + CGU_REG_LCR); 439 return 0; 440 } 441 442 static void jz4770_cgu_pm_resume(void) 443 { 444 u32 val; 445 446 val = readl(cgu->base + CGU_REG_LCR); 447 writel(val & ~LCR_LPM, cgu->base + CGU_REG_LCR); 448 } 449 450 static struct syscore_ops jz4770_cgu_pm_ops = { 451 .suspend = jz4770_cgu_pm_suspend, 452 .resume = jz4770_cgu_pm_resume, 453 }; 454 #endif /* CONFIG_PM_SLEEP */ 455 456 static void __init jz4770_cgu_init(struct device_node *np) 457 { 458 int retval; 459 460 cgu = ingenic_cgu_new(jz4770_cgu_clocks, 461 ARRAY_SIZE(jz4770_cgu_clocks), np); 462 if (!cgu) 463 pr_err("%s: failed to initialise CGU\n", __func__); 464 465 retval = ingenic_cgu_register_clocks(cgu); 466 if (retval) 467 pr_err("%s: failed to register CGU Clocks\n", __func__); 468 469 #if IS_ENABLED(CONFIG_PM_SLEEP) 470 register_syscore_ops(&jz4770_cgu_pm_ops); 471 #endif 472 } 473 474 /* We only probe via devicetree, no need for a platform driver */ 475 CLK_OF_DECLARE(jz4770_cgu, "ingenic,jz4770-cgu", jz4770_cgu_init); 476