162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Ingenic JZ4740 SoC CGU driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015 Imagination Technologies 662306a36Sopenharmony_ci * Author: Paul Burton <paul.burton@mips.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/delay.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <dt-bindings/clock/ingenic,jz4740-cgu.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "cgu.h" 1762306a36Sopenharmony_ci#include "pm.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* CGU register offsets */ 2062306a36Sopenharmony_ci#define CGU_REG_CPCCR 0x00 2162306a36Sopenharmony_ci#define CGU_REG_LCR 0x04 2262306a36Sopenharmony_ci#define CGU_REG_CPPCR 0x10 2362306a36Sopenharmony_ci#define CGU_REG_CLKGR 0x20 2462306a36Sopenharmony_ci#define CGU_REG_SCR 0x24 2562306a36Sopenharmony_ci#define CGU_REG_I2SCDR 0x60 2662306a36Sopenharmony_ci#define CGU_REG_LPCDR 0x64 2762306a36Sopenharmony_ci#define CGU_REG_MSCCDR 0x68 2862306a36Sopenharmony_ci#define CGU_REG_UHCCDR 0x6c 2962306a36Sopenharmony_ci#define CGU_REG_SSICDR 0x74 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* bits within a PLL control register */ 3262306a36Sopenharmony_ci#define PLLCTL_M_SHIFT 23 3362306a36Sopenharmony_ci#define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT) 3462306a36Sopenharmony_ci#define PLLCTL_N_SHIFT 18 3562306a36Sopenharmony_ci#define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT) 3662306a36Sopenharmony_ci#define PLLCTL_OD_SHIFT 16 3762306a36Sopenharmony_ci#define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT) 3862306a36Sopenharmony_ci#define PLLCTL_STABLE (1 << 10) 3962306a36Sopenharmony_ci#define PLLCTL_BYPASS (1 << 9) 4062306a36Sopenharmony_ci#define PLLCTL_ENABLE (1 << 8) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* bits within the LCR register */ 4362306a36Sopenharmony_ci#define LCR_SLEEP (1 << 0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* bits within the CLKGR register */ 4662306a36Sopenharmony_ci#define CLKGR_UDC (1 << 11) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct ingenic_cgu *cgu; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const s8 pll_od_encoding[4] = { 5162306a36Sopenharmony_ci 0x0, 0x1, -1, 0x3, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic const u8 jz4740_cgu_cpccr_div_table[] = { 5562306a36Sopenharmony_ci 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic const u8 jz4740_cgu_pll_half_div_table[] = { 5962306a36Sopenharmony_ci 2, 1, 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* External clocks */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci [JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT }, 6762306a36Sopenharmony_ci [JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT }, 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci [JZ4740_CLK_PLL] = { 7062306a36Sopenharmony_ci "pll", CGU_CLK_PLL, 7162306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 7262306a36Sopenharmony_ci .pll = { 7362306a36Sopenharmony_ci .reg = CGU_REG_CPPCR, 7462306a36Sopenharmony_ci .rate_multiplier = 1, 7562306a36Sopenharmony_ci .m_shift = 23, 7662306a36Sopenharmony_ci .m_bits = 9, 7762306a36Sopenharmony_ci .m_offset = 2, 7862306a36Sopenharmony_ci .n_shift = 18, 7962306a36Sopenharmony_ci .n_bits = 5, 8062306a36Sopenharmony_ci .n_offset = 2, 8162306a36Sopenharmony_ci .od_shift = 16, 8262306a36Sopenharmony_ci .od_bits = 2, 8362306a36Sopenharmony_ci .od_max = 4, 8462306a36Sopenharmony_ci .od_encoding = pll_od_encoding, 8562306a36Sopenharmony_ci .stable_bit = 10, 8662306a36Sopenharmony_ci .bypass_reg = CGU_REG_CPPCR, 8762306a36Sopenharmony_ci .bypass_bit = 9, 8862306a36Sopenharmony_ci .enable_bit = 8, 8962306a36Sopenharmony_ci }, 9062306a36Sopenharmony_ci }, 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Muxes & dividers */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci [JZ4740_CLK_PLL_HALF] = { 9562306a36Sopenharmony_ci "pll half", CGU_CLK_DIV, 9662306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 9762306a36Sopenharmony_ci .div = { 9862306a36Sopenharmony_ci CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1, 0, 9962306a36Sopenharmony_ci jz4740_cgu_pll_half_div_table, 10062306a36Sopenharmony_ci }, 10162306a36Sopenharmony_ci }, 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci [JZ4740_CLK_CCLK] = { 10462306a36Sopenharmony_ci "cclk", CGU_CLK_DIV, 10562306a36Sopenharmony_ci /* 10662306a36Sopenharmony_ci * Disabling the CPU clock or any parent clocks will hang the 10762306a36Sopenharmony_ci * system; mark it critical. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci .flags = CLK_IS_CRITICAL, 11062306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 11162306a36Sopenharmony_ci .div = { 11262306a36Sopenharmony_ci CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 0, 11362306a36Sopenharmony_ci jz4740_cgu_cpccr_div_table, 11462306a36Sopenharmony_ci }, 11562306a36Sopenharmony_ci }, 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci [JZ4740_CLK_HCLK] = { 11862306a36Sopenharmony_ci "hclk", CGU_CLK_DIV, 11962306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 12062306a36Sopenharmony_ci .div = { 12162306a36Sopenharmony_ci CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 0, 12262306a36Sopenharmony_ci jz4740_cgu_cpccr_div_table, 12362306a36Sopenharmony_ci }, 12462306a36Sopenharmony_ci }, 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci [JZ4740_CLK_PCLK] = { 12762306a36Sopenharmony_ci "pclk", CGU_CLK_DIV, 12862306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 12962306a36Sopenharmony_ci .div = { 13062306a36Sopenharmony_ci CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 0, 13162306a36Sopenharmony_ci jz4740_cgu_cpccr_div_table, 13262306a36Sopenharmony_ci }, 13362306a36Sopenharmony_ci }, 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci [JZ4740_CLK_MCLK] = { 13662306a36Sopenharmony_ci "mclk", CGU_CLK_DIV, 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Disabling MCLK or its parents will render DRAM 13962306a36Sopenharmony_ci * inaccessible; mark it critical. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci .flags = CLK_IS_CRITICAL, 14262306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 14362306a36Sopenharmony_ci .div = { 14462306a36Sopenharmony_ci CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 0, 14562306a36Sopenharmony_ci jz4740_cgu_cpccr_div_table, 14662306a36Sopenharmony_ci }, 14762306a36Sopenharmony_ci }, 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci [JZ4740_CLK_LCD] = { 15062306a36Sopenharmony_ci "lcd", CGU_CLK_DIV | CGU_CLK_GATE, 15162306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 15262306a36Sopenharmony_ci .div = { 15362306a36Sopenharmony_ci CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1, 0, 15462306a36Sopenharmony_ci jz4740_cgu_cpccr_div_table, 15562306a36Sopenharmony_ci }, 15662306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 10 }, 15762306a36Sopenharmony_ci }, 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci [JZ4740_CLK_LCD_PCLK] = { 16062306a36Sopenharmony_ci "lcd_pclk", CGU_CLK_DIV, 16162306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 16262306a36Sopenharmony_ci .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 }, 16362306a36Sopenharmony_ci }, 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci [JZ4740_CLK_I2S] = { 16662306a36Sopenharmony_ci "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 16762306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, 16862306a36Sopenharmony_ci .mux = { CGU_REG_CPCCR, 31, 1 }, 16962306a36Sopenharmony_ci .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 }, 17062306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 6 }, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci [JZ4740_CLK_SPI] = { 17462306a36Sopenharmony_ci "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 17562306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 }, 17662306a36Sopenharmony_ci .mux = { CGU_REG_SSICDR, 31, 1 }, 17762306a36Sopenharmony_ci .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 }, 17862306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 4 }, 17962306a36Sopenharmony_ci }, 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci [JZ4740_CLK_MMC] = { 18262306a36Sopenharmony_ci "mmc", CGU_CLK_DIV | CGU_CLK_GATE, 18362306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 18462306a36Sopenharmony_ci .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 }, 18562306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 7 }, 18662306a36Sopenharmony_ci }, 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci [JZ4740_CLK_UHC] = { 18962306a36Sopenharmony_ci "uhc", CGU_CLK_DIV | CGU_CLK_GATE, 19062306a36Sopenharmony_ci .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 19162306a36Sopenharmony_ci .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 }, 19262306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 14 }, 19362306a36Sopenharmony_ci }, 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci [JZ4740_CLK_UDC] = { 19662306a36Sopenharmony_ci "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 19762306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, 19862306a36Sopenharmony_ci .mux = { CGU_REG_CPCCR, 29, 1 }, 19962306a36Sopenharmony_ci .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, 20062306a36Sopenharmony_ci .gate = { CGU_REG_SCR, 6, true }, 20162306a36Sopenharmony_ci }, 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Gate-only clocks */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci [JZ4740_CLK_UART0] = { 20662306a36Sopenharmony_ci "uart0", CGU_CLK_GATE, 20762306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 20862306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 0 }, 20962306a36Sopenharmony_ci }, 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci [JZ4740_CLK_UART1] = { 21262306a36Sopenharmony_ci "uart1", CGU_CLK_GATE, 21362306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 21462306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 15 }, 21562306a36Sopenharmony_ci }, 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci [JZ4740_CLK_DMA] = { 21862306a36Sopenharmony_ci "dma", CGU_CLK_GATE, 21962306a36Sopenharmony_ci .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, 22062306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 12 }, 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci [JZ4740_CLK_IPU] = { 22462306a36Sopenharmony_ci "ipu", CGU_CLK_GATE, 22562306a36Sopenharmony_ci .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, 22662306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 13 }, 22762306a36Sopenharmony_ci }, 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci [JZ4740_CLK_ADC] = { 23062306a36Sopenharmony_ci "adc", CGU_CLK_GATE, 23162306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 23262306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 8 }, 23362306a36Sopenharmony_ci }, 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci [JZ4740_CLK_I2C] = { 23662306a36Sopenharmony_ci "i2c", CGU_CLK_GATE, 23762306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 23862306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 3 }, 23962306a36Sopenharmony_ci }, 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci [JZ4740_CLK_AIC] = { 24262306a36Sopenharmony_ci "aic", CGU_CLK_GATE, 24362306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 24462306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 5 }, 24562306a36Sopenharmony_ci }, 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci [JZ4740_CLK_TCU] = { 24862306a36Sopenharmony_ci "tcu", CGU_CLK_GATE, 24962306a36Sopenharmony_ci .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 25062306a36Sopenharmony_ci .gate = { CGU_REG_CLKGR, 1 }, 25162306a36Sopenharmony_ci }, 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void __init jz4740_cgu_init(struct device_node *np) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int retval; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci cgu = ingenic_cgu_new(jz4740_cgu_clocks, 25962306a36Sopenharmony_ci ARRAY_SIZE(jz4740_cgu_clocks), np); 26062306a36Sopenharmony_ci if (!cgu) { 26162306a36Sopenharmony_ci pr_err("%s: failed to initialise CGU\n", __func__); 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci retval = ingenic_cgu_register_clocks(cgu); 26662306a36Sopenharmony_ci if (retval) 26762306a36Sopenharmony_ci pr_err("%s: failed to register CGU Clocks\n", __func__); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci ingenic_cgu_register_syscore_ops(cgu); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ciCLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); 272