447 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * JZ4760 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 <linux/clk.h>14 15#include <dt-bindings/clock/ingenic,jz4760-cgu.h>16 17#include "cgu.h"18#include "pm.h"19 20#define MHZ (1000 * 1000)21 22/*23 * CPM registers offset address definition24 */25#define CGU_REG_CPCCR 0x0026#define CGU_REG_LCR 0x0427#define CGU_REG_CPPCR0 0x1028#define CGU_REG_CLKGR0 0x2029#define CGU_REG_OPCR 0x2430#define CGU_REG_CLKGR1 0x2831#define CGU_REG_CPPCR1 0x3032#define CGU_REG_USBPCR 0x3c33#define CGU_REG_USBCDR 0x5034#define CGU_REG_I2SCDR 0x6035#define CGU_REG_LPCDR 0x6436#define CGU_REG_MSCCDR 0x6837#define CGU_REG_UHCCDR 0x6c38#define CGU_REG_SSICDR 0x7439#define CGU_REG_CIMCDR 0x7c40#define CGU_REG_GPSCDR 0x8041#define CGU_REG_PCMCDR 0x8442#define CGU_REG_GPUCDR 0x8843 44static const s8 pll_od_encoding[8] = {45 0x0, 0x1, -1, 0x2, -1, -1, -1, 0x3,46};47 48static const u8 jz4760_cgu_cpccr_div_table[] = {49 1, 2, 3, 4, 6, 8,50};51 52static const u8 jz4760_cgu_pll_half_div_table[] = {53 2, 1,54};55 56static void57jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,58 unsigned long rate, unsigned long parent_rate,59 unsigned int *pm, unsigned int *pn, unsigned int *pod)60{61 unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 1;62 63 /* The frequency after the N divider must be between 1 and 50 MHz. */64 n = parent_rate / (1 * MHZ);65 66 /* The N divider must be >= 2. */67 n = clamp_val(n, 2, 1 << pll_info->n_bits);68 69 rate /= MHZ;70 parent_rate /= MHZ;71 72 for (m = m_max; m >= m_max && n >= 2; n--) {73 m = rate * n / parent_rate;74 od = m & 1;75 m <<= od;76 }77 78 *pm = m;79 *pn = n + 1;80 *pod = 1 << od;81}82 83static const struct ingenic_cgu_clk_info jz4760_cgu_clocks[] = {84 85 /* External clocks */86 87 [JZ4760_CLK_EXT] = { "ext", CGU_CLK_EXT },88 [JZ4760_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT },89 90 /* PLLs */91 92 [JZ4760_CLK_PLL0] = {93 "pll0", CGU_CLK_PLL,94 .parents = { JZ4760_CLK_EXT },95 .pll = {96 .reg = CGU_REG_CPPCR0,97 .rate_multiplier = 1,98 .m_shift = 23,99 .m_bits = 8,100 .m_offset = 0,101 .n_shift = 18,102 .n_bits = 4,103 .n_offset = 0,104 .od_shift = 16,105 .od_bits = 2,106 .od_max = 8,107 .od_encoding = pll_od_encoding,108 .bypass_reg = CGU_REG_CPPCR0,109 .bypass_bit = 9,110 .enable_bit = 8,111 .stable_bit = 10,112 .calc_m_n_od = jz4760_cgu_calc_m_n_od,113 },114 },115 116 [JZ4760_CLK_PLL1] = {117 /* TODO: PLL1 can depend on PLL0 */118 "pll1", CGU_CLK_PLL,119 .parents = { JZ4760_CLK_EXT },120 .pll = {121 .reg = CGU_REG_CPPCR1,122 .rate_multiplier = 1,123 .m_shift = 23,124 .m_bits = 8,125 .m_offset = 0,126 .n_shift = 18,127 .n_bits = 4,128 .n_offset = 0,129 .od_shift = 16,130 .od_bits = 2,131 .od_max = 8,132 .od_encoding = pll_od_encoding,133 .bypass_bit = -1,134 .enable_bit = 7,135 .stable_bit = 6,136 .calc_m_n_od = jz4760_cgu_calc_m_n_od,137 },138 },139 140 /* Main clocks */141 142 [JZ4760_CLK_CCLK] = {143 "cclk", CGU_CLK_DIV,144 /*145 * Disabling the CPU clock or any parent clocks will hang the146 * system; mark it critical.147 */148 .flags = CLK_IS_CRITICAL,149 .parents = { JZ4760_CLK_PLL0, },150 .div = {151 CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,152 jz4760_cgu_cpccr_div_table,153 },154 },155 [JZ4760_CLK_HCLK] = {156 "hclk", CGU_CLK_DIV,157 .parents = { JZ4760_CLK_PLL0, },158 .div = {159 CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,160 jz4760_cgu_cpccr_div_table,161 },162 },163 [JZ4760_CLK_SCLK] = {164 "sclk", CGU_CLK_DIV,165 .parents = { JZ4760_CLK_PLL0, },166 .div = {167 CGU_REG_CPCCR, 24, 1, 4, 22, -1, -1, 0,168 jz4760_cgu_cpccr_div_table,169 },170 },171 [JZ4760_CLK_H2CLK] = {172 "h2clk", CGU_CLK_DIV,173 .parents = { JZ4760_CLK_PLL0, },174 .div = {175 CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0,176 jz4760_cgu_cpccr_div_table,177 },178 },179 [JZ4760_CLK_MCLK] = {180 "mclk", CGU_CLK_DIV,181 /*182 * Disabling MCLK or its parents will render DRAM183 * inaccessible; mark it critical.184 */185 .flags = CLK_IS_CRITICAL,186 .parents = { JZ4760_CLK_PLL0, },187 .div = {188 CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,189 jz4760_cgu_cpccr_div_table,190 },191 },192 [JZ4760_CLK_PCLK] = {193 "pclk", CGU_CLK_DIV,194 .parents = { JZ4760_CLK_PLL0, },195 .div = {196 CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,197 jz4760_cgu_cpccr_div_table,198 },199 },200 201 /* Divided clocks */202 203 [JZ4760_CLK_PLL0_HALF] = {204 "pll0_half", CGU_CLK_DIV,205 .parents = { JZ4760_CLK_PLL0 },206 .div = {207 CGU_REG_CPCCR, 21, 1, 1, 22, -1, -1, 0,208 jz4760_cgu_pll_half_div_table,209 },210 },211 212 /* Those divided clocks can connect to PLL0 or PLL1 */213 214 [JZ4760_CLK_UHC] = {215 "uhc", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,216 .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, },217 .mux = { CGU_REG_UHCCDR, 31, 1 },218 .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 },219 .gate = { CGU_REG_CLKGR0, 24 },220 },221 [JZ4760_CLK_GPU] = {222 "gpu", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,223 .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, },224 .mux = { CGU_REG_GPUCDR, 31, 1 },225 .div = { CGU_REG_GPUCDR, 0, 1, 3, -1, -1, -1 },226 .gate = { CGU_REG_CLKGR1, 9 },227 },228 [JZ4760_CLK_LPCLK_DIV] = {229 "lpclk_div", CGU_CLK_DIV | CGU_CLK_MUX,230 .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, },231 .mux = { CGU_REG_LPCDR, 29, 1 },232 .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },233 },234 [JZ4760_CLK_TVE] = {235 "tve", CGU_CLK_GATE | CGU_CLK_MUX,236 .parents = { JZ4760_CLK_LPCLK_DIV, JZ4760_CLK_EXT, },237 .mux = { CGU_REG_LPCDR, 31, 1 },238 .gate = { CGU_REG_CLKGR0, 27 },239 },240 [JZ4760_CLK_LPCLK] = {241 "lpclk", CGU_CLK_GATE | CGU_CLK_MUX,242 .parents = { JZ4760_CLK_LPCLK_DIV, JZ4760_CLK_TVE, },243 .mux = { CGU_REG_LPCDR, 30, 1 },244 .gate = { CGU_REG_CLKGR0, 28 },245 },246 [JZ4760_CLK_GPS] = {247 "gps", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,248 .parents = { JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1, },249 .mux = { CGU_REG_GPSCDR, 31, 1 },250 .div = { CGU_REG_GPSCDR, 0, 1, 4, -1, -1, -1 },251 .gate = { CGU_REG_CLKGR0, 22 },252 },253 254 /* Those divided clocks can connect to EXT, PLL0 or PLL1 */255 256 [JZ4760_CLK_PCM] = {257 "pcm", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,258 .parents = { JZ4760_CLK_EXT, -1,259 JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1 },260 .mux = { CGU_REG_PCMCDR, 30, 2 },261 .div = { CGU_REG_PCMCDR, 0, 1, 9, -1, -1, -1, BIT(0) },262 .gate = { CGU_REG_CLKGR1, 8 },263 },264 [JZ4760_CLK_I2S] = {265 "i2s", CGU_CLK_DIV | CGU_CLK_MUX,266 .parents = { JZ4760_CLK_EXT, -1,267 JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1 },268 .mux = { CGU_REG_I2SCDR, 30, 2 },269 .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1, BIT(0) },270 },271 [JZ4760_CLK_OTG] = {272 "usb", CGU_CLK_DIV | CGU_CLK_GATE | CGU_CLK_MUX,273 .parents = { JZ4760_CLK_EXT, -1,274 JZ4760_CLK_PLL0_HALF, JZ4760_CLK_PLL1 },275 .mux = { CGU_REG_USBCDR, 30, 2 },276 .div = { CGU_REG_USBCDR, 0, 1, 8, -1, -1, -1 },277 .gate = { CGU_REG_CLKGR0, 2 },278 },279 280 /* Those divided clocks can connect to EXT or PLL0 */281 [JZ4760_CLK_MMC_MUX] = {282 "mmc_mux", CGU_CLK_MUX | CGU_CLK_DIV,283 .parents = { JZ4760_CLK_EXT, JZ4760_CLK_PLL0_HALF, },284 .mux = { CGU_REG_MSCCDR, 31, 1 },285 .div = { CGU_REG_MSCCDR, 0, 1, 6, -1, -1, -1, BIT(0) },286 },287 [JZ4760_CLK_SSI_MUX] = {288 "ssi_mux", CGU_CLK_DIV | CGU_CLK_MUX,289 .parents = { JZ4760_CLK_EXT, JZ4760_CLK_PLL0_HALF, },290 .mux = { CGU_REG_SSICDR, 31, 1 },291 .div = { CGU_REG_SSICDR, 0, 1, 6, -1, -1, -1, BIT(0) },292 },293 294 /* These divided clock can connect to PLL0 only */295 [JZ4760_CLK_CIM] = {296 "cim", CGU_CLK_DIV | CGU_CLK_GATE,297 .parents = { JZ4760_CLK_PLL0_HALF },298 .div = { CGU_REG_CIMCDR, 0, 1, 8, -1, -1, -1 },299 .gate = { CGU_REG_CLKGR0, 26 },300 },301 302 /* Gate-only clocks */303 304 [JZ4760_CLK_SSI0] = {305 "ssi0", CGU_CLK_GATE,306 .parents = { JZ4760_CLK_SSI_MUX, },307 .gate = { CGU_REG_CLKGR0, 4 },308 },309 [JZ4760_CLK_SSI1] = {310 "ssi1", CGU_CLK_GATE,311 .parents = { JZ4760_CLK_SSI_MUX, },312 .gate = { CGU_REG_CLKGR0, 19 },313 },314 [JZ4760_CLK_SSI2] = {315 "ssi2", CGU_CLK_GATE,316 .parents = { JZ4760_CLK_SSI_MUX, },317 .gate = { CGU_REG_CLKGR0, 20 },318 },319 [JZ4760_CLK_DMA] = {320 "dma", CGU_CLK_GATE,321 .parents = { JZ4760_CLK_H2CLK, },322 .gate = { CGU_REG_CLKGR0, 21 },323 },324 [JZ4760_CLK_MDMA] = {325 "mdma", CGU_CLK_GATE,326 .parents = { JZ4760_CLK_HCLK, },327 .gate = { CGU_REG_CLKGR0, 25 },328 },329 [JZ4760_CLK_BDMA] = {330 "bdma", CGU_CLK_GATE,331 .parents = { JZ4760_CLK_HCLK, },332 .gate = { CGU_REG_CLKGR1, 0 },333 },334 [JZ4760_CLK_I2C0] = {335 "i2c0", CGU_CLK_GATE,336 .parents = { JZ4760_CLK_EXT, },337 .gate = { CGU_REG_CLKGR0, 5 },338 },339 [JZ4760_CLK_I2C1] = {340 "i2c1", CGU_CLK_GATE,341 .parents = { JZ4760_CLK_EXT, },342 .gate = { CGU_REG_CLKGR0, 6 },343 },344 [JZ4760_CLK_UART0] = {345 "uart0", CGU_CLK_GATE,346 .parents = { JZ4760_CLK_EXT, },347 .gate = { CGU_REG_CLKGR0, 15 },348 },349 [JZ4760_CLK_UART1] = {350 "uart1", CGU_CLK_GATE,351 .parents = { JZ4760_CLK_EXT, },352 .gate = { CGU_REG_CLKGR0, 16 },353 },354 [JZ4760_CLK_UART2] = {355 "uart2", CGU_CLK_GATE,356 .parents = { JZ4760_CLK_EXT, },357 .gate = { CGU_REG_CLKGR0, 17 },358 },359 [JZ4760_CLK_UART3] = {360 "uart3", CGU_CLK_GATE,361 .parents = { JZ4760_CLK_EXT, },362 .gate = { CGU_REG_CLKGR0, 18 },363 },364 [JZ4760_CLK_IPU] = {365 "ipu", CGU_CLK_GATE,366 .parents = { JZ4760_CLK_HCLK, },367 .gate = { CGU_REG_CLKGR0, 29 },368 },369 [JZ4760_CLK_ADC] = {370 "adc", CGU_CLK_GATE,371 .parents = { JZ4760_CLK_EXT, },372 .gate = { CGU_REG_CLKGR0, 14 },373 },374 [JZ4760_CLK_AIC] = {375 "aic", CGU_CLK_GATE,376 .parents = { JZ4760_CLK_EXT, },377 .gate = { CGU_REG_CLKGR0, 8 },378 },379 [JZ4760_CLK_VPU] = {380 "vpu", CGU_CLK_GATE,381 .parents = { JZ4760_CLK_HCLK, },382 .gate = { CGU_REG_LCR, 30, false, 150 },383 },384 [JZ4760_CLK_MMC0] = {385 "mmc0", CGU_CLK_GATE,386 .parents = { JZ4760_CLK_MMC_MUX, },387 .gate = { CGU_REG_CLKGR0, 3 },388 },389 [JZ4760_CLK_MMC1] = {390 "mmc1", CGU_CLK_GATE,391 .parents = { JZ4760_CLK_MMC_MUX, },392 .gate = { CGU_REG_CLKGR0, 11 },393 },394 [JZ4760_CLK_MMC2] = {395 "mmc2", CGU_CLK_GATE,396 .parents = { JZ4760_CLK_MMC_MUX, },397 .gate = { CGU_REG_CLKGR0, 12 },398 },399 [JZ4760_CLK_UHC_PHY] = {400 "uhc_phy", CGU_CLK_GATE,401 .parents = { JZ4760_CLK_UHC, },402 .gate = { CGU_REG_OPCR, 5 },403 },404 [JZ4760_CLK_OTG_PHY] = {405 "usb_phy", CGU_CLK_GATE,406 .parents = { JZ4760_CLK_OTG },407 .gate = { CGU_REG_OPCR, 7, true, 50 },408 },409 410 /* Custom clocks */411 [JZ4760_CLK_EXT512] = {412 "ext/512", CGU_CLK_FIXDIV,413 .parents = { JZ4760_CLK_EXT },414 .fixdiv = { 512 },415 },416 [JZ4760_CLK_RTC] = {417 "rtc", CGU_CLK_MUX,418 .parents = { JZ4760_CLK_EXT512, JZ4760_CLK_OSC32K, },419 .mux = { CGU_REG_OPCR, 2, 1},420 },421};422 423static void __init jz4760_cgu_init(struct device_node *np)424{425 struct ingenic_cgu *cgu;426 int retval;427 428 cgu = ingenic_cgu_new(jz4760_cgu_clocks,429 ARRAY_SIZE(jz4760_cgu_clocks), np);430 if (!cgu) {431 pr_err("%s: failed to initialise CGU\n", __func__);432 return;433 }434 435 retval = ingenic_cgu_register_clocks(cgu);436 if (retval)437 pr_err("%s: failed to register CGU Clocks\n", __func__);438 439 ingenic_cgu_register_syscore_ops(cgu);440}441 442/* We only probe via devicetree, no need for a platform driver */443CLK_OF_DECLARE_DRIVER(jz4760_cgu, "ingenic,jz4760-cgu", jz4760_cgu_init);444 445/* JZ4760B has some small differences, but we don't implement them. */446CLK_OF_DECLARE_DRIVER(jz4760b_cgu, "ingenic,jz4760b-cgu", jz4760_cgu_init);447