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