1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Ingenic JZ4725B SoC CGU driver 4 * 5 * Copyright (C) 2018 Paul Cercueil 6 * Author: Paul Cercueil <paul@crapouillou.net> 7 */ 8 9 #include <linux/clk-provider.h> 10 #include <linux/delay.h> 11 #include <linux/of.h> 12 13 #include <dt-bindings/clock/ingenic,jz4725b-cgu.h> 14 15 #include "cgu.h" 16 #include "pm.h" 17 18 /* CGU register offsets */ 19 #define CGU_REG_CPCCR 0x00 20 #define CGU_REG_LCR 0x04 21 #define CGU_REG_CPPCR 0x10 22 #define CGU_REG_CLKGR 0x20 23 #define CGU_REG_OPCR 0x24 24 #define CGU_REG_I2SCDR 0x60 25 #define CGU_REG_LPCDR 0x64 26 #define CGU_REG_MSCCDR 0x68 27 #define CGU_REG_SSICDR 0x74 28 #define CGU_REG_CIMCDR 0x78 29 30 /* bits within the LCR register */ 31 #define LCR_SLEEP BIT(0) 32 33 static struct ingenic_cgu *cgu; 34 35 static const s8 pll_od_encoding[4] = { 36 0x0, 0x1, -1, 0x3, 37 }; 38 39 static const u8 jz4725b_cgu_cpccr_div_table[] = { 40 1, 2, 3, 4, 6, 8, 41 }; 42 43 static const u8 jz4725b_cgu_pll_half_div_table[] = { 44 2, 1, 45 }; 46 47 static const struct ingenic_cgu_clk_info jz4725b_cgu_clocks[] = { 48 49 /* External clocks */ 50 51 [JZ4725B_CLK_EXT] = { "ext", CGU_CLK_EXT }, 52 [JZ4725B_CLK_OSC32K] = { "osc32k", CGU_CLK_EXT }, 53 54 [JZ4725B_CLK_PLL] = { 55 "pll", CGU_CLK_PLL, 56 .parents = { JZ4725B_CLK_EXT, -1, -1, -1 }, 57 .pll = { 58 .reg = CGU_REG_CPPCR, 59 .rate_multiplier = 1, 60 .m_shift = 23, 61 .m_bits = 9, 62 .m_offset = 2, 63 .n_shift = 18, 64 .n_bits = 5, 65 .n_offset = 2, 66 .od_shift = 16, 67 .od_bits = 2, 68 .od_max = 4, 69 .od_encoding = pll_od_encoding, 70 .stable_bit = 10, 71 .bypass_reg = CGU_REG_CPPCR, 72 .bypass_bit = 9, 73 .enable_bit = 8, 74 }, 75 }, 76 77 /* Muxes & dividers */ 78 79 [JZ4725B_CLK_PLL_HALF] = { 80 "pll half", CGU_CLK_DIV, 81 .parents = { JZ4725B_CLK_PLL, -1, -1, -1 }, 82 .div = { 83 CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0, 84 jz4725b_cgu_pll_half_div_table, 85 }, 86 }, 87 88 [JZ4725B_CLK_CCLK] = { 89 "cclk", CGU_CLK_DIV, 90 /* 91 * Disabling the CPU clock or any parent clocks will hang the 92 * system; mark it critical. 93 */ 94 .flags = CLK_IS_CRITICAL, 95 .parents = { JZ4725B_CLK_PLL, -1, -1, -1 }, 96 .div = { 97 CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0, 98 jz4725b_cgu_cpccr_div_table, 99 }, 100 }, 101 102 [JZ4725B_CLK_HCLK] = { 103 "hclk", CGU_CLK_DIV, 104 .parents = { JZ4725B_CLK_PLL, -1, -1, -1 }, 105 .div = { 106 CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0, 107 jz4725b_cgu_cpccr_div_table, 108 }, 109 }, 110 111 [JZ4725B_CLK_PCLK] = { 112 "pclk", CGU_CLK_DIV, 113 .parents = { JZ4725B_CLK_PLL, -1, -1, -1 }, 114 .div = { 115 CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0, 116 jz4725b_cgu_cpccr_div_table, 117 }, 118 }, 119 120 [JZ4725B_CLK_MCLK] = { 121 "mclk", CGU_CLK_DIV, 122 /* 123 * Disabling MCLK or its parents will render DRAM 124 * inaccessible; mark it critical. 125 */ 126 .flags = CLK_IS_CRITICAL, 127 .parents = { JZ4725B_CLK_PLL, -1, -1, -1 }, 128 .div = { 129 CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0, 130 jz4725b_cgu_cpccr_div_table, 131 }, 132 }, 133 134 [JZ4725B_CLK_IPU] = { 135 "ipu", CGU_CLK_DIV | CGU_CLK_GATE, 136 .parents = { JZ4725B_CLK_PLL, -1, -1, -1 }, 137 .div = { 138 CGU_REG_CPCCR, 16, 1, 4, 22, -1, -1, 0, 139 jz4725b_cgu_cpccr_div_table, 140 }, 141 .gate = { CGU_REG_CLKGR, 13 }, 142 }, 143 144 [JZ4725B_CLK_LCD] = { 145 "lcd", CGU_CLK_DIV | CGU_CLK_GATE, 146 .parents = { JZ4725B_CLK_PLL_HALF, -1, -1, -1 }, 147 .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 }, 148 .gate = { CGU_REG_CLKGR, 9 }, 149 }, 150 151 [JZ4725B_CLK_I2S] = { 152 "i2s", CGU_CLK_MUX | CGU_CLK_DIV, 153 .parents = { JZ4725B_CLK_EXT, JZ4725B_CLK_PLL_HALF, -1, -1 }, 154 .mux = { CGU_REG_CPCCR, 31, 1 }, 155 .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 }, 156 }, 157 158 [JZ4725B_CLK_SPI] = { 159 "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 160 .parents = { JZ4725B_CLK_EXT, JZ4725B_CLK_PLL, -1, -1 }, 161 .mux = { CGU_REG_SSICDR, 31, 1 }, 162 .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 }, 163 .gate = { CGU_REG_CLKGR, 4 }, 164 }, 165 166 [JZ4725B_CLK_MMC_MUX] = { 167 "mmc_mux", CGU_CLK_DIV, 168 .parents = { JZ4725B_CLK_PLL_HALF, -1, -1, -1 }, 169 .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 }, 170 }, 171 172 [JZ4725B_CLK_UDC] = { 173 "udc", CGU_CLK_MUX | CGU_CLK_DIV, 174 .parents = { JZ4725B_CLK_EXT, JZ4725B_CLK_PLL_HALF, -1, -1 }, 175 .mux = { CGU_REG_CPCCR, 29, 1 }, 176 .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, 177 }, 178 179 /* Gate-only clocks */ 180 181 [JZ4725B_CLK_UART] = { 182 "uart", CGU_CLK_GATE, 183 .parents = { JZ4725B_CLK_EXT, -1, -1, -1 }, 184 .gate = { CGU_REG_CLKGR, 0 }, 185 }, 186 187 [JZ4725B_CLK_DMA] = { 188 "dma", CGU_CLK_GATE, 189 .parents = { JZ4725B_CLK_PCLK, -1, -1, -1 }, 190 .gate = { CGU_REG_CLKGR, 12 }, 191 }, 192 193 [JZ4725B_CLK_ADC] = { 194 "adc", CGU_CLK_GATE, 195 .parents = { JZ4725B_CLK_EXT, -1, -1, -1 }, 196 .gate = { CGU_REG_CLKGR, 7 }, 197 }, 198 199 [JZ4725B_CLK_I2C] = { 200 "i2c", CGU_CLK_GATE, 201 .parents = { JZ4725B_CLK_EXT, -1, -1, -1 }, 202 .gate = { CGU_REG_CLKGR, 3 }, 203 }, 204 205 [JZ4725B_CLK_AIC] = { 206 "aic", CGU_CLK_GATE, 207 .parents = { JZ4725B_CLK_EXT, -1, -1, -1 }, 208 .gate = { CGU_REG_CLKGR, 5 }, 209 }, 210 211 [JZ4725B_CLK_MMC0] = { 212 "mmc0", CGU_CLK_GATE, 213 .parents = { JZ4725B_CLK_MMC_MUX, -1, -1, -1 }, 214 .gate = { CGU_REG_CLKGR, 6 }, 215 }, 216 217 [JZ4725B_CLK_MMC1] = { 218 "mmc1", CGU_CLK_GATE, 219 .parents = { JZ4725B_CLK_MMC_MUX, -1, -1, -1 }, 220 .gate = { CGU_REG_CLKGR, 16 }, 221 }, 222 223 [JZ4725B_CLK_BCH] = { 224 "bch", CGU_CLK_GATE, 225 .parents = { JZ4725B_CLK_MCLK/* not sure */, -1, -1, -1 }, 226 .gate = { CGU_REG_CLKGR, 11 }, 227 }, 228 229 [JZ4725B_CLK_TCU] = { 230 "tcu", CGU_CLK_GATE, 231 .parents = { JZ4725B_CLK_EXT/* not sure */, -1, -1, -1 }, 232 .gate = { CGU_REG_CLKGR, 1 }, 233 }, 234 235 [JZ4725B_CLK_EXT512] = { 236 "ext/512", CGU_CLK_FIXDIV, 237 .parents = { JZ4725B_CLK_EXT }, 238 239 /* Doc calls it EXT512, but it seems to be /256... */ 240 .fixdiv = { 256 }, 241 }, 242 243 [JZ4725B_CLK_RTC] = { 244 "rtc", CGU_CLK_MUX, 245 .parents = { JZ4725B_CLK_EXT512, JZ4725B_CLK_OSC32K, -1, -1 }, 246 .mux = { CGU_REG_OPCR, 2, 1}, 247 }, 248 249 [JZ4725B_CLK_UDC_PHY] = { 250 "udc_phy", CGU_CLK_GATE, 251 .parents = { JZ4725B_CLK_EXT, -1, -1, -1 }, 252 .gate = { CGU_REG_OPCR, 6, true }, 253 }, 254 }; 255 256 static void __init jz4725b_cgu_init(struct device_node *np) 257 { 258 int retval; 259 260 cgu = ingenic_cgu_new(jz4725b_cgu_clocks, 261 ARRAY_SIZE(jz4725b_cgu_clocks), np); 262 if (!cgu) { 263 pr_err("%s: failed to initialise CGU\n", __func__); 264 return; 265 } 266 267 retval = ingenic_cgu_register_clocks(cgu); 268 if (retval) 269 pr_err("%s: failed to register CGU Clocks\n", __func__); 270 271 ingenic_cgu_register_syscore_ops(cgu); 272 } 273 CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-cgu", jz4725b_cgu_init); 274