464 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * JZ4770 SoC CGU driver4 * 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 definition20 */21#define CGU_REG_CPCCR 0x0022#define CGU_REG_LCR 0x0423#define CGU_REG_CPPCR0 0x1024#define CGU_REG_CLKGR0 0x2025#define CGU_REG_OPCR 0x2426#define CGU_REG_CLKGR1 0x2827#define CGU_REG_CPPCR1 0x3028#define CGU_REG_USBPCR1 0x4829#define CGU_REG_USBCDR 0x5030#define CGU_REG_I2SCDR 0x6031#define CGU_REG_LPCDR 0x6432#define CGU_REG_MSC0CDR 0x6833#define CGU_REG_UHCCDR 0x6c34#define CGU_REG_SSICDR 0x7435#define CGU_REG_CIMCDR 0x7c36#define CGU_REG_GPSCDR 0x8037#define CGU_REG_PCMCDR 0x8438#define CGU_REG_GPUCDR 0x8839#define CGU_REG_MSC1CDR 0xA440#define CGU_REG_MSC2CDR 0xA841#define CGU_REG_BCHCDR 0xAC42 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 49static struct ingenic_cgu *cgu;50 51static 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 61static 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 70static 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 79static 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 85static const s8 pll_od_encoding[8] = {86 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,87};88 89static const u8 jz4770_cgu_cpccr_div_table[] = {90 1, 2, 3, 4, 6, 8, 12,91};92 93static 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 /*153 * Disabling the CPU clock or any parent clocks will hang the154 * system; mark it critical.155 */156 .flags = CLK_IS_CRITICAL,157 .parents = { JZ4770_CLK_PLL0, },158 .div = {159 CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,160 jz4770_cgu_cpccr_div_table,161 },162 },163 [JZ4770_CLK_H0CLK] = {164 "h0clk", CGU_CLK_DIV,165 .parents = { JZ4770_CLK_PLL0, },166 .div = {167 CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,168 jz4770_cgu_cpccr_div_table,169 },170 },171 [JZ4770_CLK_H1CLK] = {172 "h1clk", CGU_CLK_DIV | CGU_CLK_GATE,173 .parents = { JZ4770_CLK_PLL0, },174 .div = {175 CGU_REG_CPCCR, 24, 1, 4, 22, -1, -1, 0,176 jz4770_cgu_cpccr_div_table,177 },178 .gate = { CGU_REG_CLKGR1, 7 },179 },180 [JZ4770_CLK_H2CLK] = {181 "h2clk", CGU_CLK_DIV,182 .parents = { JZ4770_CLK_PLL0, },183 .div = {184 CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,185 jz4770_cgu_cpccr_div_table,186 },187 },188 [JZ4770_CLK_C1CLK] = {189 "c1clk", CGU_CLK_DIV | CGU_CLK_GATE,190 .parents = { JZ4770_CLK_PLL0, },191 .div = {192 CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,193 jz4770_cgu_cpccr_div_table,194 },195 .gate = { CGU_REG_OPCR, 31, true }, // disable CCLK stop on idle196 },197 [JZ4770_CLK_PCLK] = {198 "pclk", CGU_CLK_DIV,199 .parents = { JZ4770_CLK_PLL0, },200 .div = {201 CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,202 jz4770_cgu_cpccr_div_table,203 },204 },205 206 /* Those divided clocks can connect to PLL0 or PLL1 */207 208 [JZ4770_CLK_MMC0_MUX] = {209 "mmc0_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,210 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },211 .mux = { CGU_REG_MSC0CDR, 30, 1 },212 .div = { CGU_REG_MSC0CDR, 0, 1, 7, -1, -1, 31 },213 .gate = { CGU_REG_MSC0CDR, 31 },214 },215 [JZ4770_CLK_MMC1_MUX] = {216 "mmc1_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,217 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },218 .mux = { CGU_REG_MSC1CDR, 30, 1 },219 .div = { CGU_REG_MSC1CDR, 0, 1, 7, -1, -1, 31 },220 .gate = { CGU_REG_MSC1CDR, 31 },221 },222 [JZ4770_CLK_MMC2_MUX] = {223 "mmc2_mux", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,224 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },225 .mux = { CGU_REG_MSC2CDR, 30, 1 },226 .div = { CGU_REG_MSC2CDR, 0, 1, 7, -1, -1, 31 },227 .gate = { CGU_REG_MSC2CDR, 31 },228 },229 [JZ4770_CLK_CIM] = {230 "cim", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,231 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },232 .mux = { CGU_REG_CIMCDR, 31, 1 },233 .div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 },234 .gate = { CGU_REG_CLKGR0, 26 },235 },236 [JZ4770_CLK_UHC] = {237 "uhc", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,238 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },239 .mux = { CGU_REG_UHCCDR, 29, 1 },240 .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 },241 .gate = { CGU_REG_CLKGR0, 24 },242 },243 [JZ4770_CLK_GPU] = {244 "gpu", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,245 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, -1 },246 .mux = { CGU_REG_GPUCDR, 31, 1 },247 .div = { CGU_REG_GPUCDR, 0, 1, 3, -1, -1, -1 },248 .gate = { CGU_REG_CLKGR1, 9 },249 },250 [JZ4770_CLK_BCH] = {251 "bch", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,252 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },253 .mux = { CGU_REG_BCHCDR, 31, 1 },254 .div = { CGU_REG_BCHCDR, 0, 1, 3, -1, -1, -1 },255 .gate = { CGU_REG_CLKGR0, 1 },256 },257 [JZ4770_CLK_LPCLK_MUX] = {258 "lpclk", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,259 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },260 .mux = { CGU_REG_LPCDR, 29, 1 },261 .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },262 .gate = { CGU_REG_CLKGR0, 28 },263 },264 [JZ4770_CLK_GPS] = {265 "gps", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,266 .parents = { JZ4770_CLK_PLL0, JZ4770_CLK_PLL1, },267 .mux = { CGU_REG_GPSCDR, 31, 1 },268 .div = { CGU_REG_GPSCDR, 0, 1, 4, -1, -1, -1 },269 .gate = { CGU_REG_CLKGR0, 22 },270 },271 272 /* Those divided clocks can connect to EXT, PLL0 or PLL1 */273 274 [JZ4770_CLK_SSI_MUX] = {275 "ssi_mux", CGU_CLK_DIV | CGU_CLK_MUX,276 .parents = { JZ4770_CLK_EXT, -1,277 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },278 .mux = { CGU_REG_SSICDR, 30, 2 },279 .div = { CGU_REG_SSICDR, 0, 1, 6, -1, -1, -1 },280 },281 [JZ4770_CLK_PCM_MUX] = {282 "pcm_mux", CGU_CLK_DIV | CGU_CLK_MUX,283 .parents = { JZ4770_CLK_EXT, -1,284 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },285 .mux = { CGU_REG_PCMCDR, 30, 2 },286 .div = { CGU_REG_PCMCDR, 0, 1, 9, -1, -1, -1 },287 },288 [JZ4770_CLK_I2S] = {289 "i2s", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,290 .parents = { JZ4770_CLK_EXT, -1,291 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },292 .mux = { CGU_REG_I2SCDR, 30, 2 },293 .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },294 .gate = { CGU_REG_CLKGR1, 13 },295 },296 [JZ4770_CLK_OTG] = {297 "usb", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,298 .parents = { JZ4770_CLK_EXT, -1,299 JZ4770_CLK_PLL0, JZ4770_CLK_PLL1 },300 .mux = { CGU_REG_USBCDR, 30, 2 },301 .div = { CGU_REG_USBCDR, 0, 1, 8, -1, -1, -1 },302 .gate = { CGU_REG_CLKGR0, 2 },303 },304 305 /* Gate-only clocks */306 307 [JZ4770_CLK_SSI0] = {308 "ssi0", CGU_CLK_GATE,309 .parents = { JZ4770_CLK_SSI_MUX, },310 .gate = { CGU_REG_CLKGR0, 4 },311 },312 [JZ4770_CLK_SSI1] = {313 "ssi1", CGU_CLK_GATE,314 .parents = { JZ4770_CLK_SSI_MUX, },315 .gate = { CGU_REG_CLKGR0, 19 },316 },317 [JZ4770_CLK_SSI2] = {318 "ssi2", CGU_CLK_GATE,319 .parents = { JZ4770_CLK_SSI_MUX, },320 .gate = { CGU_REG_CLKGR0, 20 },321 },322 [JZ4770_CLK_PCM0] = {323 "pcm0", CGU_CLK_GATE,324 .parents = { JZ4770_CLK_PCM_MUX, },325 .gate = { CGU_REG_CLKGR1, 8 },326 },327 [JZ4770_CLK_PCM1] = {328 "pcm1", CGU_CLK_GATE,329 .parents = { JZ4770_CLK_PCM_MUX, },330 .gate = { CGU_REG_CLKGR1, 10 },331 },332 [JZ4770_CLK_DMA] = {333 "dma", CGU_CLK_GATE,334 .parents = { JZ4770_CLK_H2CLK, },335 .gate = { CGU_REG_CLKGR0, 21 },336 },337 [JZ4770_CLK_BDMA] = {338 "bdma", CGU_CLK_GATE,339 .parents = { JZ4770_CLK_H2CLK, },340 .gate = { CGU_REG_CLKGR1, 0 },341 },342 [JZ4770_CLK_I2C0] = {343 "i2c0", CGU_CLK_GATE,344 .parents = { JZ4770_CLK_EXT, },345 .gate = { CGU_REG_CLKGR0, 5 },346 },347 [JZ4770_CLK_I2C1] = {348 "i2c1", CGU_CLK_GATE,349 .parents = { JZ4770_CLK_EXT, },350 .gate = { CGU_REG_CLKGR0, 6 },351 },352 [JZ4770_CLK_I2C2] = {353 "i2c2", CGU_CLK_GATE,354 .parents = { JZ4770_CLK_EXT, },355 .gate = { CGU_REG_CLKGR1, 15 },356 },357 [JZ4770_CLK_UART0] = {358 "uart0", CGU_CLK_GATE,359 .parents = { JZ4770_CLK_EXT, },360 .gate = { CGU_REG_CLKGR0, 15 },361 },362 [JZ4770_CLK_UART1] = {363 "uart1", CGU_CLK_GATE,364 .parents = { JZ4770_CLK_EXT, },365 .gate = { CGU_REG_CLKGR0, 16 },366 },367 [JZ4770_CLK_UART2] = {368 "uart2", CGU_CLK_GATE,369 .parents = { JZ4770_CLK_EXT, },370 .gate = { CGU_REG_CLKGR0, 17 },371 },372 [JZ4770_CLK_UART3] = {373 "uart3", CGU_CLK_GATE,374 .parents = { JZ4770_CLK_EXT, },375 .gate = { CGU_REG_CLKGR0, 18 },376 },377 [JZ4770_CLK_IPU] = {378 "ipu", CGU_CLK_GATE,379 .parents = { JZ4770_CLK_H0CLK, },380 .gate = { CGU_REG_CLKGR0, 29 },381 },382 [JZ4770_CLK_ADC] = {383 "adc", CGU_CLK_GATE,384 .parents = { JZ4770_CLK_EXT, },385 .gate = { CGU_REG_CLKGR0, 14 },386 },387 [JZ4770_CLK_AIC] = {388 "aic", CGU_CLK_GATE,389 .parents = { JZ4770_CLK_EXT, },390 .gate = { CGU_REG_CLKGR0, 8 },391 },392 [JZ4770_CLK_AUX] = {393 "aux", CGU_CLK_GATE,394 .parents = { JZ4770_CLK_C1CLK, },395 .gate = { CGU_REG_CLKGR1, 14 },396 },397 [JZ4770_CLK_VPU] = {398 "vpu", CGU_CLK_GATE,399 .parents = { JZ4770_CLK_H1CLK, },400 .gate = { CGU_REG_LCR, 30, false, 150 },401 },402 [JZ4770_CLK_MMC0] = {403 "mmc0", CGU_CLK_GATE,404 .parents = { JZ4770_CLK_MMC0_MUX, },405 .gate = { CGU_REG_CLKGR0, 3 },406 },407 [JZ4770_CLK_MMC1] = {408 "mmc1", CGU_CLK_GATE,409 .parents = { JZ4770_CLK_MMC1_MUX, },410 .gate = { CGU_REG_CLKGR0, 11 },411 },412 [JZ4770_CLK_MMC2] = {413 "mmc2", CGU_CLK_GATE,414 .parents = { JZ4770_CLK_MMC2_MUX, },415 .gate = { CGU_REG_CLKGR0, 12 },416 },417 [JZ4770_CLK_OTG_PHY] = {418 "usb_phy", CGU_CLK_GATE,419 .parents = { JZ4770_CLK_OTG },420 .gate = { CGU_REG_OPCR, 7, true, 50 },421 },422 423 /* Custom clocks */424 425 [JZ4770_CLK_UHC_PHY] = {426 "uhc_phy", CGU_CLK_CUSTOM,427 .parents = { JZ4770_CLK_UHC, -1, -1, -1 },428 .custom = { &jz4770_uhc_phy_ops },429 },430 431 [JZ4770_CLK_EXT512] = {432 "ext/512", CGU_CLK_FIXDIV,433 .parents = { JZ4770_CLK_EXT },434 .fixdiv = { 512 },435 },436 437 [JZ4770_CLK_RTC] = {438 "rtc", CGU_CLK_MUX,439 .parents = { JZ4770_CLK_EXT512, JZ4770_CLK_OSC32K, },440 .mux = { CGU_REG_OPCR, 2, 1},441 },442};443 444static void __init jz4770_cgu_init(struct device_node *np)445{446 int retval;447 448 cgu = ingenic_cgu_new(jz4770_cgu_clocks,449 ARRAY_SIZE(jz4770_cgu_clocks), np);450 if (!cgu) {451 pr_err("%s: failed to initialise CGU\n", __func__);452 return;453 }454 455 retval = ingenic_cgu_register_clocks(cgu);456 if (retval)457 pr_err("%s: failed to register CGU Clocks\n", __func__);458 459 ingenic_cgu_register_syscore_ops(cgu);460}461 462/* We only probe via devicetree, no need for a platform driver */463CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-cgu", jz4770_cgu_init);464