272 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Ingenic JZ4740 SoC CGU driver4 *5 * Copyright (c) 2015 Imagination Technologies6 * Author: Paul Burton <paul.burton@mips.com>7 */8 9#include <linux/clk-provider.h>10#include <linux/delay.h>11#include <linux/io.h>12#include <linux/of.h>13 14#include <dt-bindings/clock/ingenic,jz4740-cgu.h>15 16#include "cgu.h"17#include "pm.h"18 19/* CGU register offsets */20#define CGU_REG_CPCCR 0x0021#define CGU_REG_LCR 0x0422#define CGU_REG_CPPCR 0x1023#define CGU_REG_CLKGR 0x2024#define CGU_REG_SCR 0x2425#define CGU_REG_I2SCDR 0x6026#define CGU_REG_LPCDR 0x6427#define CGU_REG_MSCCDR 0x6828#define CGU_REG_UHCCDR 0x6c29#define CGU_REG_SSICDR 0x7430 31/* bits within a PLL control register */32#define PLLCTL_M_SHIFT 2333#define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT)34#define PLLCTL_N_SHIFT 1835#define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT)36#define PLLCTL_OD_SHIFT 1637#define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT)38#define PLLCTL_STABLE (1 << 10)39#define PLLCTL_BYPASS (1 << 9)40#define PLLCTL_ENABLE (1 << 8)41 42/* bits within the LCR register */43#define LCR_SLEEP (1 << 0)44 45/* bits within the CLKGR register */46#define CLKGR_UDC (1 << 11)47 48static struct ingenic_cgu *cgu;49 50static const s8 pll_od_encoding[4] = {51 0x0, 0x1, -1, 0x3,52};53 54static const u8 jz4740_cgu_cpccr_div_table[] = {55 1, 2, 3, 4, 6, 8, 12, 16, 24, 32,56};57 58static const u8 jz4740_cgu_pll_half_div_table[] = {59 2, 1,60};61 62static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {63 64 /* External clocks */65 66 [JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT },67 [JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT },68 69 [JZ4740_CLK_PLL] = {70 "pll", CGU_CLK_PLL,71 .parents = { JZ4740_CLK_EXT, -1, -1, -1 },72 .pll = {73 .reg = CGU_REG_CPPCR,74 .rate_multiplier = 1,75 .m_shift = 23,76 .m_bits = 9,77 .m_offset = 2,78 .n_shift = 18,79 .n_bits = 5,80 .n_offset = 2,81 .od_shift = 16,82 .od_bits = 2,83 .od_max = 4,84 .od_encoding = pll_od_encoding,85 .stable_bit = 10,86 .bypass_reg = CGU_REG_CPPCR,87 .bypass_bit = 9,88 .enable_bit = 8,89 },90 },91 92 /* Muxes & dividers */93 94 [JZ4740_CLK_PLL_HALF] = {95 "pll half", CGU_CLK_DIV,96 .parents = { JZ4740_CLK_PLL, -1, -1, -1 },97 .div = {98 CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0,99 jz4740_cgu_pll_half_div_table,100 },101 },102 103 [JZ4740_CLK_CCLK] = {104 "cclk", CGU_CLK_DIV,105 /*106 * Disabling the CPU clock or any parent clocks will hang the107 * system; mark it critical.108 */109 .flags = CLK_IS_CRITICAL,110 .parents = { JZ4740_CLK_PLL, -1, -1, -1 },111 .div = {112 CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0,113 jz4740_cgu_cpccr_div_table,114 },115 },116 117 [JZ4740_CLK_HCLK] = {118 "hclk", CGU_CLK_DIV,119 .parents = { JZ4740_CLK_PLL, -1, -1, -1 },120 .div = {121 CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0,122 jz4740_cgu_cpccr_div_table,123 },124 },125 126 [JZ4740_CLK_PCLK] = {127 "pclk", CGU_CLK_DIV,128 .parents = { JZ4740_CLK_PLL, -1, -1, -1 },129 .div = {130 CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0,131 jz4740_cgu_cpccr_div_table,132 },133 },134 135 [JZ4740_CLK_MCLK] = {136 "mclk", CGU_CLK_DIV,137 /*138 * Disabling MCLK or its parents will render DRAM139 * inaccessible; mark it critical.140 */141 .flags = CLK_IS_CRITICAL,142 .parents = { JZ4740_CLK_PLL, -1, -1, -1 },143 .div = {144 CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0,145 jz4740_cgu_cpccr_div_table,146 },147 },148 149 [JZ4740_CLK_LCD] = {150 "lcd", CGU_CLK_DIV | CGU_CLK_GATE,151 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },152 .div = {153 CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1, 0,154 jz4740_cgu_cpccr_div_table,155 },156 .gate = { CGU_REG_CLKGR, 10 },157 },158 159 [JZ4740_CLK_LCD_PCLK] = {160 "lcd_pclk", CGU_CLK_DIV,161 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },162 .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 },163 },164 165 [JZ4740_CLK_I2S] = {166 "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,167 .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },168 .mux = { CGU_REG_CPCCR, 31, 1 },169 .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 },170 .gate = { CGU_REG_CLKGR, 6 },171 },172 173 [JZ4740_CLK_SPI] = {174 "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,175 .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 },176 .mux = { CGU_REG_SSICDR, 31, 1 },177 .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 },178 .gate = { CGU_REG_CLKGR, 4 },179 },180 181 [JZ4740_CLK_MMC] = {182 "mmc", CGU_CLK_DIV | CGU_CLK_GATE,183 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },184 .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 },185 .gate = { CGU_REG_CLKGR, 7 },186 },187 188 [JZ4740_CLK_UHC] = {189 "uhc", CGU_CLK_DIV | CGU_CLK_GATE,190 .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },191 .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 },192 .gate = { CGU_REG_CLKGR, 14 },193 },194 195 [JZ4740_CLK_UDC] = {196 "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,197 .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },198 .mux = { CGU_REG_CPCCR, 29, 1 },199 .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 },200 .gate = { CGU_REG_SCR, 6, true },201 },202 203 /* Gate-only clocks */204 205 [JZ4740_CLK_UART0] = {206 "uart0", CGU_CLK_GATE,207 .parents = { JZ4740_CLK_EXT, -1, -1, -1 },208 .gate = { CGU_REG_CLKGR, 0 },209 },210 211 [JZ4740_CLK_UART1] = {212 "uart1", CGU_CLK_GATE,213 .parents = { JZ4740_CLK_EXT, -1, -1, -1 },214 .gate = { CGU_REG_CLKGR, 15 },215 },216 217 [JZ4740_CLK_DMA] = {218 "dma", CGU_CLK_GATE,219 .parents = { JZ4740_CLK_PCLK, -1, -1, -1 },220 .gate = { CGU_REG_CLKGR, 12 },221 },222 223 [JZ4740_CLK_IPU] = {224 "ipu", CGU_CLK_GATE,225 .parents = { JZ4740_CLK_PCLK, -1, -1, -1 },226 .gate = { CGU_REG_CLKGR, 13 },227 },228 229 [JZ4740_CLK_ADC] = {230 "adc", CGU_CLK_GATE,231 .parents = { JZ4740_CLK_EXT, -1, -1, -1 },232 .gate = { CGU_REG_CLKGR, 8 },233 },234 235 [JZ4740_CLK_I2C] = {236 "i2c", CGU_CLK_GATE,237 .parents = { JZ4740_CLK_EXT, -1, -1, -1 },238 .gate = { CGU_REG_CLKGR, 3 },239 },240 241 [JZ4740_CLK_AIC] = {242 "aic", CGU_CLK_GATE,243 .parents = { JZ4740_CLK_EXT, -1, -1, -1 },244 .gate = { CGU_REG_CLKGR, 5 },245 },246 247 [JZ4740_CLK_TCU] = {248 "tcu", CGU_CLK_GATE,249 .parents = { JZ4740_CLK_EXT, -1, -1, -1 },250 .gate = { CGU_REG_CLKGR, 1 },251 },252};253 254static void __init jz4740_cgu_init(struct device_node *np)255{256 int retval;257 258 cgu = ingenic_cgu_new(jz4740_cgu_clocks,259 ARRAY_SIZE(jz4740_cgu_clocks), np);260 if (!cgu) {261 pr_err("%s: failed to initialise CGU\n", __func__);262 return;263 }264 265 retval = ingenic_cgu_register_clocks(cgu);266 if (retval)267 pr_err("%s: failed to register CGU Clocks\n", __func__);268 269 ingenic_cgu_register_syscore_ops(cgu);270}271CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init);272