| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Ingenic JZ4740 SoC CGU driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2015 Imagination Technologies |
|---|
| 5 | 6 | * 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 | 7 | */ |
|---|
| 17 | 8 | |
|---|
| 18 | 9 | #include <linux/clk-provider.h> |
|---|
| 19 | 10 | #include <linux/delay.h> |
|---|
| 11 | +#include <linux/io.h> |
|---|
| 20 | 12 | #include <linux/of.h> |
|---|
| 13 | + |
|---|
| 21 | 14 | #include <dt-bindings/clock/jz4740-cgu.h> |
|---|
| 22 | | -#include <asm/mach-jz4740/clock.h> |
|---|
| 15 | + |
|---|
| 23 | 16 | #include "cgu.h" |
|---|
| 17 | +#include "pm.h" |
|---|
| 24 | 18 | |
|---|
| 25 | 19 | /* CGU register offsets */ |
|---|
| 26 | 20 | #define CGU_REG_CPCCR 0x00 |
|---|
| .. | .. |
|---|
| 57 | 51 | 0x0, 0x1, -1, 0x3, |
|---|
| 58 | 52 | }; |
|---|
| 59 | 53 | |
|---|
| 54 | +static const u8 jz4740_cgu_cpccr_div_table[] = { |
|---|
| 55 | + 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, |
|---|
| 56 | +}; |
|---|
| 57 | + |
|---|
| 58 | +static const u8 jz4740_cgu_pll_half_div_table[] = { |
|---|
| 59 | + 2, 1, |
|---|
| 60 | +}; |
|---|
| 61 | + |
|---|
| 60 | 62 | static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { |
|---|
| 61 | 63 | |
|---|
| 62 | 64 | /* External clocks */ |
|---|
| .. | .. |
|---|
| 69 | 71 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, |
|---|
| 70 | 72 | .pll = { |
|---|
| 71 | 73 | .reg = CGU_REG_CPPCR, |
|---|
| 74 | + .rate_multiplier = 1, |
|---|
| 72 | 75 | .m_shift = 23, |
|---|
| 73 | 76 | .m_bits = 9, |
|---|
| 74 | 77 | .m_offset = 2, |
|---|
| .. | .. |
|---|
| 80 | 83 | .od_max = 4, |
|---|
| 81 | 84 | .od_encoding = pll_od_encoding, |
|---|
| 82 | 85 | .stable_bit = 10, |
|---|
| 86 | + .bypass_reg = CGU_REG_CPPCR, |
|---|
| 83 | 87 | .bypass_bit = 9, |
|---|
| 84 | 88 | .enable_bit = 8, |
|---|
| 85 | 89 | }, |
|---|
| .. | .. |
|---|
| 90 | 94 | [JZ4740_CLK_PLL_HALF] = { |
|---|
| 91 | 95 | "pll half", CGU_CLK_DIV, |
|---|
| 92 | 96 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, |
|---|
| 93 | | - .div = { CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1 }, |
|---|
| 97 | + .div = { |
|---|
| 98 | + CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, |
|---|
| 99 | + jz4740_cgu_pll_half_div_table, |
|---|
| 100 | + }, |
|---|
| 94 | 101 | }, |
|---|
| 95 | 102 | |
|---|
| 96 | 103 | [JZ4740_CLK_CCLK] = { |
|---|
| 97 | 104 | "cclk", CGU_CLK_DIV, |
|---|
| 98 | 105 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, |
|---|
| 99 | | - .div = { CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1 }, |
|---|
| 106 | + .div = { |
|---|
| 107 | + CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, |
|---|
| 108 | + jz4740_cgu_cpccr_div_table, |
|---|
| 109 | + }, |
|---|
| 100 | 110 | }, |
|---|
| 101 | 111 | |
|---|
| 102 | 112 | [JZ4740_CLK_HCLK] = { |
|---|
| 103 | 113 | "hclk", CGU_CLK_DIV, |
|---|
| 104 | 114 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, |
|---|
| 105 | | - .div = { CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1 }, |
|---|
| 115 | + .div = { |
|---|
| 116 | + CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, |
|---|
| 117 | + jz4740_cgu_cpccr_div_table, |
|---|
| 118 | + }, |
|---|
| 106 | 119 | }, |
|---|
| 107 | 120 | |
|---|
| 108 | 121 | [JZ4740_CLK_PCLK] = { |
|---|
| 109 | 122 | "pclk", CGU_CLK_DIV, |
|---|
| 110 | 123 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, |
|---|
| 111 | | - .div = { CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1 }, |
|---|
| 124 | + .div = { |
|---|
| 125 | + CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, |
|---|
| 126 | + jz4740_cgu_cpccr_div_table, |
|---|
| 127 | + }, |
|---|
| 112 | 128 | }, |
|---|
| 113 | 129 | |
|---|
| 114 | 130 | [JZ4740_CLK_MCLK] = { |
|---|
| 115 | 131 | "mclk", CGU_CLK_DIV, |
|---|
| 116 | 132 | .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, |
|---|
| 117 | | - .div = { CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1 }, |
|---|
| 133 | + .div = { |
|---|
| 134 | + CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, |
|---|
| 135 | + jz4740_cgu_cpccr_div_table, |
|---|
| 136 | + }, |
|---|
| 118 | 137 | }, |
|---|
| 119 | 138 | |
|---|
| 120 | 139 | [JZ4740_CLK_LCD] = { |
|---|
| 121 | 140 | "lcd", CGU_CLK_DIV | CGU_CLK_GATE, |
|---|
| 122 | 141 | .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, |
|---|
| 123 | | - .div = { CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1 }, |
|---|
| 142 | + .div = { |
|---|
| 143 | + CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1, |
|---|
| 144 | + jz4740_cgu_cpccr_div_table, |
|---|
| 145 | + }, |
|---|
| 124 | 146 | .gate = { CGU_REG_CLKGR, 10 }, |
|---|
| 125 | 147 | }, |
|---|
| 126 | 148 | |
|---|
| .. | .. |
|---|
| 211 | 233 | .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, |
|---|
| 212 | 234 | .gate = { CGU_REG_CLKGR, 5 }, |
|---|
| 213 | 235 | }, |
|---|
| 236 | + |
|---|
| 237 | + [JZ4740_CLK_TCU] = { |
|---|
| 238 | + "tcu", CGU_CLK_GATE, |
|---|
| 239 | + .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, |
|---|
| 240 | + .gate = { CGU_REG_CLKGR, 1 }, |
|---|
| 241 | + }, |
|---|
| 214 | 242 | }; |
|---|
| 215 | 243 | |
|---|
| 216 | 244 | static void __init jz4740_cgu_init(struct device_node *np) |
|---|
| .. | .. |
|---|
| 227 | 255 | retval = ingenic_cgu_register_clocks(cgu); |
|---|
| 228 | 256 | if (retval) |
|---|
| 229 | 257 | pr_err("%s: failed to register CGU Clocks\n", __func__); |
|---|
| 258 | + |
|---|
| 259 | + ingenic_cgu_register_syscore_ops(cgu); |
|---|
| 230 | 260 | } |
|---|
| 231 | | -CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); |
|---|
| 232 | | - |
|---|
| 233 | | -void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) |
|---|
| 234 | | -{ |
|---|
| 235 | | - uint32_t lcr = readl(cgu->base + CGU_REG_LCR); |
|---|
| 236 | | - |
|---|
| 237 | | - switch (mode) { |
|---|
| 238 | | - case JZ4740_WAIT_MODE_IDLE: |
|---|
| 239 | | - lcr &= ~LCR_SLEEP; |
|---|
| 240 | | - break; |
|---|
| 241 | | - |
|---|
| 242 | | - case JZ4740_WAIT_MODE_SLEEP: |
|---|
| 243 | | - lcr |= LCR_SLEEP; |
|---|
| 244 | | - break; |
|---|
| 245 | | - } |
|---|
| 246 | | - |
|---|
| 247 | | - writel(lcr, cgu->base + CGU_REG_LCR); |
|---|
| 248 | | -} |
|---|
| 249 | | - |
|---|
| 250 | | -void jz4740_clock_udc_disable_auto_suspend(void) |
|---|
| 251 | | -{ |
|---|
| 252 | | - uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); |
|---|
| 253 | | - |
|---|
| 254 | | - clkgr &= ~CLKGR_UDC; |
|---|
| 255 | | - writel(clkgr, cgu->base + CGU_REG_CLKGR); |
|---|
| 256 | | -} |
|---|
| 257 | | -EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); |
|---|
| 258 | | - |
|---|
| 259 | | -void jz4740_clock_udc_enable_auto_suspend(void) |
|---|
| 260 | | -{ |
|---|
| 261 | | - uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); |
|---|
| 262 | | - |
|---|
| 263 | | - clkgr |= CLKGR_UDC; |
|---|
| 264 | | - writel(clkgr, cgu->base + CGU_REG_CLKGR); |
|---|
| 265 | | -} |
|---|
| 266 | | -EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); |
|---|
| 267 | | - |
|---|
| 268 | | -#define JZ_CLOCK_GATE_UART0 BIT(0) |
|---|
| 269 | | -#define JZ_CLOCK_GATE_TCU BIT(1) |
|---|
| 270 | | -#define JZ_CLOCK_GATE_DMAC BIT(12) |
|---|
| 271 | | - |
|---|
| 272 | | -void jz4740_clock_suspend(void) |
|---|
| 273 | | -{ |
|---|
| 274 | | - uint32_t clkgr, cppcr; |
|---|
| 275 | | - |
|---|
| 276 | | - clkgr = readl(cgu->base + CGU_REG_CLKGR); |
|---|
| 277 | | - clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0; |
|---|
| 278 | | - writel(clkgr, cgu->base + CGU_REG_CLKGR); |
|---|
| 279 | | - |
|---|
| 280 | | - cppcr = readl(cgu->base + CGU_REG_CPPCR); |
|---|
| 281 | | - cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); |
|---|
| 282 | | - writel(cppcr, cgu->base + CGU_REG_CPPCR); |
|---|
| 283 | | -} |
|---|
| 284 | | - |
|---|
| 285 | | -void jz4740_clock_resume(void) |
|---|
| 286 | | -{ |
|---|
| 287 | | - uint32_t clkgr, cppcr, stable; |
|---|
| 288 | | - |
|---|
| 289 | | - cppcr = readl(cgu->base + CGU_REG_CPPCR); |
|---|
| 290 | | - cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); |
|---|
| 291 | | - writel(cppcr, cgu->base + CGU_REG_CPPCR); |
|---|
| 292 | | - |
|---|
| 293 | | - stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit); |
|---|
| 294 | | - do { |
|---|
| 295 | | - cppcr = readl(cgu->base + CGU_REG_CPPCR); |
|---|
| 296 | | - } while (!(cppcr & stable)); |
|---|
| 297 | | - |
|---|
| 298 | | - clkgr = readl(cgu->base + CGU_REG_CLKGR); |
|---|
| 299 | | - clkgr &= ~JZ_CLOCK_GATE_TCU; |
|---|
| 300 | | - clkgr &= ~JZ_CLOCK_GATE_DMAC; |
|---|
| 301 | | - clkgr &= ~JZ_CLOCK_GATE_UART0; |
|---|
| 302 | | - writel(clkgr, cgu->base + CGU_REG_CLKGR); |
|---|
| 303 | | -} |
|---|
| 261 | +CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); |
|---|