1 /* 2 * Ingenic JZ4740 SoC CGU driver 3 * 4 * Copyright (c) 2015 Imagination Technologies 5 * Author: Paul Burton <paul.burton@mips.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 #include <linux/clk-provider.h> 19 #include <linux/delay.h> 20 #include <linux/io.h> 21 #include <linux/of.h> 22 #include <dt-bindings/clock/jz4740-cgu.h> 23 #include <asm/mach-jz4740/clock.h> 24 #include "cgu.h" 25 #include "pm.h" 26 27 /* CGU register offsets */ 28 #define CGU_REG_CPCCR 0x00 29 #define CGU_REG_LCR 0x04 30 #define CGU_REG_CPPCR 0x10 31 #define CGU_REG_CLKGR 0x20 32 #define CGU_REG_SCR 0x24 33 #define CGU_REG_I2SCDR 0x60 34 #define CGU_REG_LPCDR 0x64 35 #define CGU_REG_MSCCDR 0x68 36 #define CGU_REG_UHCCDR 0x6c 37 #define CGU_REG_SSICDR 0x74 38 39 /* bits within a PLL control register */ 40 #define PLLCTL_M_SHIFT 23 41 #define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT) 42 #define PLLCTL_N_SHIFT 18 43 #define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT) 44 #define PLLCTL_OD_SHIFT 16 45 #define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT) 46 #define PLLCTL_STABLE (1 << 10) 47 #define PLLCTL_BYPASS (1 << 9) 48 #define PLLCTL_ENABLE (1 << 8) 49 50 /* bits within the LCR register */ 51 #define LCR_SLEEP (1 << 0) 52 53 /* bits within the CLKGR register */ 54 #define CLKGR_UDC (1 << 11) 55 56 static struct ingenic_cgu *cgu; 57 58 static const s8 pll_od_encoding[4] = { 59 0x0, 0x1, -1, 0x3, 60 }; 61 62 static const u8 jz4740_cgu_cpccr_div_table[] = { 63 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 64 }; 65 66 static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { 67 68 /* External clocks */ 69 70 [JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT }, 71 [JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT }, 72 73 [JZ4740_CLK_PLL] = { 74 "pll", CGU_CLK_PLL, 75 .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 76 .pll = { 77 .reg = CGU_REG_CPPCR, 78 .m_shift = 23, 79 .m_bits = 9, 80 .m_offset = 2, 81 .n_shift = 18, 82 .n_bits = 5, 83 .n_offset = 2, 84 .od_shift = 16, 85 .od_bits = 2, 86 .od_max = 4, 87 .od_encoding = pll_od_encoding, 88 .stable_bit = 10, 89 .bypass_bit = 9, 90 .enable_bit = 8, 91 }, 92 }, 93 94 /* Muxes & dividers */ 95 96 [JZ4740_CLK_PLL_HALF] = { 97 "pll half", CGU_CLK_DIV, 98 .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 99 .div = { CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1 }, 100 }, 101 102 [JZ4740_CLK_CCLK] = { 103 "cclk", CGU_CLK_DIV, 104 .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 105 .div = { 106 CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 107 jz4740_cgu_cpccr_div_table, 108 }, 109 }, 110 111 [JZ4740_CLK_HCLK] = { 112 "hclk", CGU_CLK_DIV, 113 .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 114 .div = { 115 CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 116 jz4740_cgu_cpccr_div_table, 117 }, 118 }, 119 120 [JZ4740_CLK_PCLK] = { 121 "pclk", CGU_CLK_DIV, 122 .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 123 .div = { 124 CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 125 jz4740_cgu_cpccr_div_table, 126 }, 127 }, 128 129 [JZ4740_CLK_MCLK] = { 130 "mclk", CGU_CLK_DIV, 131 .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 132 .div = { 133 CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 134 jz4740_cgu_cpccr_div_table, 135 }, 136 }, 137 138 [JZ4740_CLK_LCD] = { 139 "lcd", CGU_CLK_DIV | CGU_CLK_GATE, 140 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 141 .div = { 142 CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1, 143 jz4740_cgu_cpccr_div_table, 144 }, 145 .gate = { CGU_REG_CLKGR, 10 }, 146 }, 147 148 [JZ4740_CLK_LCD_PCLK] = { 149 "lcd_pclk", CGU_CLK_DIV, 150 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 151 .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 }, 152 }, 153 154 [JZ4740_CLK_I2S] = { 155 "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 156 .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, 157 .mux = { CGU_REG_CPCCR, 31, 1 }, 158 .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 }, 159 .gate = { CGU_REG_CLKGR, 6 }, 160 }, 161 162 [JZ4740_CLK_SPI] = { 163 "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 164 .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 }, 165 .mux = { CGU_REG_SSICDR, 31, 1 }, 166 .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 }, 167 .gate = { CGU_REG_CLKGR, 4 }, 168 }, 169 170 [JZ4740_CLK_MMC] = { 171 "mmc", CGU_CLK_DIV | CGU_CLK_GATE, 172 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 173 .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 }, 174 .gate = { CGU_REG_CLKGR, 7 }, 175 }, 176 177 [JZ4740_CLK_UHC] = { 178 "uhc", CGU_CLK_DIV | CGU_CLK_GATE, 179 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 180 .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 }, 181 .gate = { CGU_REG_CLKGR, 14 }, 182 }, 183 184 [JZ4740_CLK_UDC] = { 185 "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 186 .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, 187 .mux = { CGU_REG_CPCCR, 29, 1 }, 188 .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, 189 .gate = { CGU_REG_SCR, 6, true }, 190 }, 191 192 /* Gate-only clocks */ 193 194 [JZ4740_CLK_UART0] = { 195 "uart0", CGU_CLK_GATE, 196 .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 197 .gate = { CGU_REG_CLKGR, 0 }, 198 }, 199 200 [JZ4740_CLK_UART1] = { 201 "uart1", CGU_CLK_GATE, 202 .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 203 .gate = { CGU_REG_CLKGR, 15 }, 204 }, 205 206 [JZ4740_CLK_DMA] = { 207 "dma", CGU_CLK_GATE, 208 .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, 209 .gate = { CGU_REG_CLKGR, 12 }, 210 }, 211 212 [JZ4740_CLK_IPU] = { 213 "ipu", CGU_CLK_GATE, 214 .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, 215 .gate = { CGU_REG_CLKGR, 13 }, 216 }, 217 218 [JZ4740_CLK_ADC] = { 219 "adc", CGU_CLK_GATE, 220 .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 221 .gate = { CGU_REG_CLKGR, 8 }, 222 }, 223 224 [JZ4740_CLK_I2C] = { 225 "i2c", CGU_CLK_GATE, 226 .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 227 .gate = { CGU_REG_CLKGR, 3 }, 228 }, 229 230 [JZ4740_CLK_AIC] = { 231 "aic", CGU_CLK_GATE, 232 .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 233 .gate = { CGU_REG_CLKGR, 5 }, 234 }, 235 }; 236 237 static void __init jz4740_cgu_init(struct device_node *np) 238 { 239 int retval; 240 241 cgu = ingenic_cgu_new(jz4740_cgu_clocks, 242 ARRAY_SIZE(jz4740_cgu_clocks), np); 243 if (!cgu) { 244 pr_err("%s: failed to initialise CGU\n", __func__); 245 return; 246 } 247 248 retval = ingenic_cgu_register_clocks(cgu); 249 if (retval) 250 pr_err("%s: failed to register CGU Clocks\n", __func__); 251 252 ingenic_cgu_register_syscore_ops(cgu); 253 } 254 CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); 255 256 void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) 257 { 258 uint32_t lcr = readl(cgu->base + CGU_REG_LCR); 259 260 switch (mode) { 261 case JZ4740_WAIT_MODE_IDLE: 262 lcr &= ~LCR_SLEEP; 263 break; 264 265 case JZ4740_WAIT_MODE_SLEEP: 266 lcr |= LCR_SLEEP; 267 break; 268 } 269 270 writel(lcr, cgu->base + CGU_REG_LCR); 271 } 272 273 void jz4740_clock_udc_disable_auto_suspend(void) 274 { 275 uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); 276 277 clkgr &= ~CLKGR_UDC; 278 writel(clkgr, cgu->base + CGU_REG_CLKGR); 279 } 280 EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); 281 282 void jz4740_clock_udc_enable_auto_suspend(void) 283 { 284 uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); 285 286 clkgr |= CLKGR_UDC; 287 writel(clkgr, cgu->base + CGU_REG_CLKGR); 288 } 289 EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); 290 291 #define JZ_CLOCK_GATE_UART0 BIT(0) 292 #define JZ_CLOCK_GATE_TCU BIT(1) 293 #define JZ_CLOCK_GATE_DMAC BIT(12) 294 295 void jz4740_clock_suspend(void) 296 { 297 uint32_t clkgr, cppcr; 298 299 clkgr = readl(cgu->base + CGU_REG_CLKGR); 300 clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0; 301 writel(clkgr, cgu->base + CGU_REG_CLKGR); 302 303 cppcr = readl(cgu->base + CGU_REG_CPPCR); 304 cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); 305 writel(cppcr, cgu->base + CGU_REG_CPPCR); 306 } 307 308 void jz4740_clock_resume(void) 309 { 310 uint32_t clkgr, cppcr, stable; 311 312 cppcr = readl(cgu->base + CGU_REG_CPPCR); 313 cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); 314 writel(cppcr, cgu->base + CGU_REG_CPPCR); 315 316 stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit); 317 do { 318 cppcr = readl(cgu->base + CGU_REG_CPPCR); 319 } while (!(cppcr & stable)); 320 321 clkgr = readl(cgu->base + CGU_REG_CLKGR); 322 clkgr &= ~JZ_CLOCK_GATE_TCU; 323 clkgr &= ~JZ_CLOCK_GATE_DMAC; 324 clkgr &= ~JZ_CLOCK_GATE_UART0; 325 writel(clkgr, cgu->base + CGU_REG_CLKGR); 326 } 327