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