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