1ff1930c6SPaul Burton /* 2ff1930c6SPaul Burton * Ingenic JZ4740 SoC CGU driver 3ff1930c6SPaul Burton * 4ff1930c6SPaul Burton * Copyright (c) 2015 Imagination Technologies 5fb615d61SPaul Burton * Author: Paul Burton <paul.burton@mips.com> 6ff1930c6SPaul Burton * 7ff1930c6SPaul Burton * This program is free software; you can redistribute it and/or 8ff1930c6SPaul Burton * modify it under the terms of the GNU General Public License as 9ff1930c6SPaul Burton * published by the Free Software Foundation; either version 2 of 10ff1930c6SPaul Burton * the License, or (at your option) any later version. 11ff1930c6SPaul Burton * 12ff1930c6SPaul Burton * This program is distributed in the hope that it will be useful, 13ff1930c6SPaul Burton * but WITHOUT ANY WARRANTY; without even the implied warranty of 14ff1930c6SPaul Burton * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15ff1930c6SPaul Burton * GNU General Public License for more details. 16ff1930c6SPaul Burton */ 17ff1930c6SPaul Burton 18ff1930c6SPaul Burton #include <linux/clk-provider.h> 19ff1930c6SPaul Burton #include <linux/delay.h> 2062e59c4eSStephen Boyd #include <linux/io.h> 21ff1930c6SPaul Burton #include <linux/of.h> 22ff1930c6SPaul Burton #include <dt-bindings/clock/jz4740-cgu.h> 2341dd641eSPaul Burton #include <asm/mach-jz4740/clock.h> 24ff1930c6SPaul Burton #include "cgu.h" 25*2ee93e3cSPaul Cercueil #include "pm.h" 26ff1930c6SPaul Burton 27ff1930c6SPaul Burton /* CGU register offsets */ 28ff1930c6SPaul Burton #define CGU_REG_CPCCR 0x00 2941dd641eSPaul Burton #define CGU_REG_LCR 0x04 30ff1930c6SPaul Burton #define CGU_REG_CPPCR 0x10 31ed286ca5SPaul Burton #define CGU_REG_CLKGR 0x20 32ff1930c6SPaul Burton #define CGU_REG_SCR 0x24 33ff1930c6SPaul Burton #define CGU_REG_I2SCDR 0x60 34ff1930c6SPaul Burton #define CGU_REG_LPCDR 0x64 35ff1930c6SPaul Burton #define CGU_REG_MSCCDR 0x68 36ff1930c6SPaul Burton #define CGU_REG_UHCCDR 0x6c 37ff1930c6SPaul Burton #define CGU_REG_SSICDR 0x74 38ff1930c6SPaul Burton 39ff1930c6SPaul Burton /* bits within a PLL control register */ 40ff1930c6SPaul Burton #define PLLCTL_M_SHIFT 23 41ff1930c6SPaul Burton #define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT) 42ff1930c6SPaul Burton #define PLLCTL_N_SHIFT 18 43ff1930c6SPaul Burton #define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT) 44ff1930c6SPaul Burton #define PLLCTL_OD_SHIFT 16 45ff1930c6SPaul Burton #define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT) 46ff1930c6SPaul Burton #define PLLCTL_STABLE (1 << 10) 47ff1930c6SPaul Burton #define PLLCTL_BYPASS (1 << 9) 48ff1930c6SPaul Burton #define PLLCTL_ENABLE (1 << 8) 49ff1930c6SPaul Burton 5041dd641eSPaul Burton /* bits within the LCR register */ 5141dd641eSPaul Burton #define LCR_SLEEP (1 << 0) 5241dd641eSPaul Burton 53ed286ca5SPaul Burton /* bits within the CLKGR register */ 54ed286ca5SPaul Burton #define CLKGR_UDC (1 << 11) 55ed286ca5SPaul Burton 56ff1930c6SPaul Burton static struct ingenic_cgu *cgu; 57ff1930c6SPaul Burton 58ff1930c6SPaul Burton static const s8 pll_od_encoding[4] = { 59ff1930c6SPaul Burton 0x0, 0x1, -1, 0x3, 60ff1930c6SPaul Burton }; 61ff1930c6SPaul Burton 622a1a7036SPaul Cercueil static const u8 jz4740_cgu_cpccr_div_table[] = { 632a1a7036SPaul Cercueil 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 642a1a7036SPaul Cercueil }; 652a1a7036SPaul Cercueil 66ff1930c6SPaul Burton static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = { 67ff1930c6SPaul Burton 68ff1930c6SPaul Burton /* External clocks */ 69ff1930c6SPaul Burton 70ff1930c6SPaul Burton [JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT }, 71ff1930c6SPaul Burton [JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT }, 72ff1930c6SPaul Burton 73ff1930c6SPaul Burton [JZ4740_CLK_PLL] = { 74ff1930c6SPaul Burton "pll", CGU_CLK_PLL, 75ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 76ff1930c6SPaul Burton .pll = { 77ff1930c6SPaul Burton .reg = CGU_REG_CPPCR, 78ff1930c6SPaul Burton .m_shift = 23, 79ff1930c6SPaul Burton .m_bits = 9, 80ff1930c6SPaul Burton .m_offset = 2, 81ff1930c6SPaul Burton .n_shift = 18, 82ff1930c6SPaul Burton .n_bits = 5, 83ff1930c6SPaul Burton .n_offset = 2, 84ff1930c6SPaul Burton .od_shift = 16, 85ff1930c6SPaul Burton .od_bits = 2, 86ff1930c6SPaul Burton .od_max = 4, 87ff1930c6SPaul Burton .od_encoding = pll_od_encoding, 88ff1930c6SPaul Burton .stable_bit = 10, 89ff1930c6SPaul Burton .bypass_bit = 9, 90ff1930c6SPaul Burton .enable_bit = 8, 91ff1930c6SPaul Burton }, 92ff1930c6SPaul Burton }, 93ff1930c6SPaul Burton 94ff1930c6SPaul Burton /* Muxes & dividers */ 95ff1930c6SPaul Burton 96ff1930c6SPaul Burton [JZ4740_CLK_PLL_HALF] = { 97ff1930c6SPaul Burton "pll half", CGU_CLK_DIV, 98ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 994afe2d1aSHarvey Hunt .div = { CGU_REG_CPCCR, 21, 1, 1, -1, -1, -1 }, 100ff1930c6SPaul Burton }, 101ff1930c6SPaul Burton 102ff1930c6SPaul Burton [JZ4740_CLK_CCLK] = { 103ff1930c6SPaul Burton "cclk", CGU_CLK_DIV, 104ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 1052a1a7036SPaul Cercueil .div = { 1062a1a7036SPaul Cercueil CGU_REG_CPCCR, 0, 1, 4, 22, -1, -1, 1072a1a7036SPaul Cercueil jz4740_cgu_cpccr_div_table, 1082a1a7036SPaul Cercueil }, 109ff1930c6SPaul Burton }, 110ff1930c6SPaul Burton 111ff1930c6SPaul Burton [JZ4740_CLK_HCLK] = { 112ff1930c6SPaul Burton "hclk", CGU_CLK_DIV, 113ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 1142a1a7036SPaul Cercueil .div = { 1152a1a7036SPaul Cercueil CGU_REG_CPCCR, 4, 1, 4, 22, -1, -1, 1162a1a7036SPaul Cercueil jz4740_cgu_cpccr_div_table, 1172a1a7036SPaul Cercueil }, 118ff1930c6SPaul Burton }, 119ff1930c6SPaul Burton 120ff1930c6SPaul Burton [JZ4740_CLK_PCLK] = { 121ff1930c6SPaul Burton "pclk", CGU_CLK_DIV, 122ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 1232a1a7036SPaul Cercueil .div = { 1242a1a7036SPaul Cercueil CGU_REG_CPCCR, 8, 1, 4, 22, -1, -1, 1252a1a7036SPaul Cercueil jz4740_cgu_cpccr_div_table, 1262a1a7036SPaul Cercueil }, 127ff1930c6SPaul Burton }, 128ff1930c6SPaul Burton 129ff1930c6SPaul Burton [JZ4740_CLK_MCLK] = { 130ff1930c6SPaul Burton "mclk", CGU_CLK_DIV, 131ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL, -1, -1, -1 }, 1322a1a7036SPaul Cercueil .div = { 1332a1a7036SPaul Cercueil CGU_REG_CPCCR, 12, 1, 4, 22, -1, -1, 1342a1a7036SPaul Cercueil jz4740_cgu_cpccr_div_table, 1352a1a7036SPaul Cercueil }, 136ff1930c6SPaul Burton }, 137ff1930c6SPaul Burton 138ff1930c6SPaul Burton [JZ4740_CLK_LCD] = { 139ff1930c6SPaul Burton "lcd", CGU_CLK_DIV | CGU_CLK_GATE, 140ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 1412a1a7036SPaul Cercueil .div = { 1422a1a7036SPaul Cercueil CGU_REG_CPCCR, 16, 1, 5, 22, -1, -1, 1432a1a7036SPaul Cercueil jz4740_cgu_cpccr_div_table, 1442a1a7036SPaul Cercueil }, 145ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 10 }, 146ff1930c6SPaul Burton }, 147ff1930c6SPaul Burton 148ff1930c6SPaul Burton [JZ4740_CLK_LCD_PCLK] = { 149ff1930c6SPaul Burton "lcd_pclk", CGU_CLK_DIV, 150ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 1514afe2d1aSHarvey Hunt .div = { CGU_REG_LPCDR, 0, 1, 11, -1, -1, -1 }, 152ff1930c6SPaul Burton }, 153ff1930c6SPaul Burton 154ff1930c6SPaul Burton [JZ4740_CLK_I2S] = { 155ff1930c6SPaul Burton "i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 156ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, 157ff1930c6SPaul Burton .mux = { CGU_REG_CPCCR, 31, 1 }, 158574f4e80SPaul Cercueil .div = { CGU_REG_I2SCDR, 0, 1, 9, -1, -1, -1 }, 159ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 6 }, 160ff1930c6SPaul Burton }, 161ff1930c6SPaul Burton 162ff1930c6SPaul Burton [JZ4740_CLK_SPI] = { 163ff1930c6SPaul Burton "spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 164ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 }, 165ff1930c6SPaul Burton .mux = { CGU_REG_SSICDR, 31, 1 }, 1664afe2d1aSHarvey Hunt .div = { CGU_REG_SSICDR, 0, 1, 4, -1, -1, -1 }, 167ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 4 }, 168ff1930c6SPaul Burton }, 169ff1930c6SPaul Burton 170ff1930c6SPaul Burton [JZ4740_CLK_MMC] = { 171ff1930c6SPaul Burton "mmc", CGU_CLK_DIV | CGU_CLK_GATE, 172ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 1734afe2d1aSHarvey Hunt .div = { CGU_REG_MSCCDR, 0, 1, 5, -1, -1, -1 }, 174ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 7 }, 175ff1930c6SPaul Burton }, 176ff1930c6SPaul Burton 177ff1930c6SPaul Burton [JZ4740_CLK_UHC] = { 178ff1930c6SPaul Burton "uhc", CGU_CLK_DIV | CGU_CLK_GATE, 179ff1930c6SPaul Burton .parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 }, 1804afe2d1aSHarvey Hunt .div = { CGU_REG_UHCCDR, 0, 1, 4, -1, -1, -1 }, 181ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 14 }, 182ff1930c6SPaul Burton }, 183ff1930c6SPaul Burton 184ff1930c6SPaul Burton [JZ4740_CLK_UDC] = { 1852b555a4bSPaul Cercueil "udc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE, 186ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 }, 187ff1930c6SPaul Burton .mux = { CGU_REG_CPCCR, 29, 1 }, 1884afe2d1aSHarvey Hunt .div = { CGU_REG_CPCCR, 23, 1, 6, -1, -1, -1 }, 189b7e29924SPaul Cercueil .gate = { CGU_REG_SCR, 6, true }, 190ff1930c6SPaul Burton }, 191ff1930c6SPaul Burton 192ff1930c6SPaul Burton /* Gate-only clocks */ 193ff1930c6SPaul Burton 194ff1930c6SPaul Burton [JZ4740_CLK_UART0] = { 195ff1930c6SPaul Burton "uart0", CGU_CLK_GATE, 196ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 197ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 0 }, 198ff1930c6SPaul Burton }, 199ff1930c6SPaul Burton 200ff1930c6SPaul Burton [JZ4740_CLK_UART1] = { 201ff1930c6SPaul Burton "uart1", CGU_CLK_GATE, 202ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 203ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 15 }, 204ff1930c6SPaul Burton }, 205ff1930c6SPaul Burton 206ff1930c6SPaul Burton [JZ4740_CLK_DMA] = { 207ff1930c6SPaul Burton "dma", CGU_CLK_GATE, 208ff1930c6SPaul Burton .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, 209ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 12 }, 210ff1930c6SPaul Burton }, 211ff1930c6SPaul Burton 212ff1930c6SPaul Burton [JZ4740_CLK_IPU] = { 213ff1930c6SPaul Burton "ipu", CGU_CLK_GATE, 214ff1930c6SPaul Burton .parents = { JZ4740_CLK_PCLK, -1, -1, -1 }, 215ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 13 }, 216ff1930c6SPaul Burton }, 217ff1930c6SPaul Burton 218ff1930c6SPaul Burton [JZ4740_CLK_ADC] = { 219ff1930c6SPaul Burton "adc", CGU_CLK_GATE, 220ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 221ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 8 }, 222ff1930c6SPaul Burton }, 223ff1930c6SPaul Burton 224ff1930c6SPaul Burton [JZ4740_CLK_I2C] = { 225ff1930c6SPaul Burton "i2c", CGU_CLK_GATE, 226ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 227ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 3 }, 228ff1930c6SPaul Burton }, 229ff1930c6SPaul Burton 230ff1930c6SPaul Burton [JZ4740_CLK_AIC] = { 231ff1930c6SPaul Burton "aic", CGU_CLK_GATE, 232ff1930c6SPaul Burton .parents = { JZ4740_CLK_EXT, -1, -1, -1 }, 233ff1930c6SPaul Burton .gate = { CGU_REG_CLKGR, 5 }, 234ff1930c6SPaul Burton }, 235ff1930c6SPaul Burton }; 236ff1930c6SPaul Burton 237ff1930c6SPaul Burton static void __init jz4740_cgu_init(struct device_node *np) 238ff1930c6SPaul Burton { 239ff1930c6SPaul Burton int retval; 240ff1930c6SPaul Burton 241ff1930c6SPaul Burton cgu = ingenic_cgu_new(jz4740_cgu_clocks, 242ff1930c6SPaul Burton ARRAY_SIZE(jz4740_cgu_clocks), np); 243ff1930c6SPaul Burton if (!cgu) { 244ff1930c6SPaul Burton pr_err("%s: failed to initialise CGU\n", __func__); 245ff1930c6SPaul Burton return; 246ff1930c6SPaul Burton } 247ff1930c6SPaul Burton 248ff1930c6SPaul Burton retval = ingenic_cgu_register_clocks(cgu); 249ff1930c6SPaul Burton if (retval) 250ff1930c6SPaul Burton pr_err("%s: failed to register CGU Clocks\n", __func__); 251*2ee93e3cSPaul Cercueil 252*2ee93e3cSPaul Cercueil ingenic_cgu_register_syscore_ops(cgu); 253ff1930c6SPaul Burton } 254ff1930c6SPaul Burton CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init); 25541dd641eSPaul Burton 25641dd641eSPaul Burton void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode) 25741dd641eSPaul Burton { 25841dd641eSPaul Burton uint32_t lcr = readl(cgu->base + CGU_REG_LCR); 25941dd641eSPaul Burton 26041dd641eSPaul Burton switch (mode) { 26141dd641eSPaul Burton case JZ4740_WAIT_MODE_IDLE: 26241dd641eSPaul Burton lcr &= ~LCR_SLEEP; 26341dd641eSPaul Burton break; 26441dd641eSPaul Burton 26541dd641eSPaul Burton case JZ4740_WAIT_MODE_SLEEP: 26641dd641eSPaul Burton lcr |= LCR_SLEEP; 26741dd641eSPaul Burton break; 26841dd641eSPaul Burton } 26941dd641eSPaul Burton 27041dd641eSPaul Burton writel(lcr, cgu->base + CGU_REG_LCR); 27141dd641eSPaul Burton } 272ed286ca5SPaul Burton 273ed286ca5SPaul Burton void jz4740_clock_udc_disable_auto_suspend(void) 274ed286ca5SPaul Burton { 275ed286ca5SPaul Burton uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); 276ed286ca5SPaul Burton 277ed286ca5SPaul Burton clkgr &= ~CLKGR_UDC; 278ed286ca5SPaul Burton writel(clkgr, cgu->base + CGU_REG_CLKGR); 279ed286ca5SPaul Burton } 280ed286ca5SPaul Burton EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend); 281ed286ca5SPaul Burton 282ed286ca5SPaul Burton void jz4740_clock_udc_enable_auto_suspend(void) 283ed286ca5SPaul Burton { 284ed286ca5SPaul Burton uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR); 285ed286ca5SPaul Burton 286ed286ca5SPaul Burton clkgr |= CLKGR_UDC; 287ed286ca5SPaul Burton writel(clkgr, cgu->base + CGU_REG_CLKGR); 288ed286ca5SPaul Burton } 289ed286ca5SPaul Burton EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend); 29050d893ffSPaul Burton 29150d893ffSPaul Burton #define JZ_CLOCK_GATE_UART0 BIT(0) 29250d893ffSPaul Burton #define JZ_CLOCK_GATE_TCU BIT(1) 29350d893ffSPaul Burton #define JZ_CLOCK_GATE_DMAC BIT(12) 29450d893ffSPaul Burton 29550d893ffSPaul Burton void jz4740_clock_suspend(void) 29650d893ffSPaul Burton { 29750d893ffSPaul Burton uint32_t clkgr, cppcr; 29850d893ffSPaul Burton 29950d893ffSPaul Burton clkgr = readl(cgu->base + CGU_REG_CLKGR); 30050d893ffSPaul Burton clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0; 30150d893ffSPaul Burton writel(clkgr, cgu->base + CGU_REG_CLKGR); 30250d893ffSPaul Burton 30350d893ffSPaul Burton cppcr = readl(cgu->base + CGU_REG_CPPCR); 30450d893ffSPaul Burton cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); 30550d893ffSPaul Burton writel(cppcr, cgu->base + CGU_REG_CPPCR); 30650d893ffSPaul Burton } 30750d893ffSPaul Burton 30850d893ffSPaul Burton void jz4740_clock_resume(void) 30950d893ffSPaul Burton { 31050d893ffSPaul Burton uint32_t clkgr, cppcr, stable; 31150d893ffSPaul Burton 31250d893ffSPaul Burton cppcr = readl(cgu->base + CGU_REG_CPPCR); 31350d893ffSPaul Burton cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit); 31450d893ffSPaul Burton writel(cppcr, cgu->base + CGU_REG_CPPCR); 31550d893ffSPaul Burton 31650d893ffSPaul Burton stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit); 31750d893ffSPaul Burton do { 31850d893ffSPaul Burton cppcr = readl(cgu->base + CGU_REG_CPPCR); 31950d893ffSPaul Burton } while (!(cppcr & stable)); 32050d893ffSPaul Burton 32150d893ffSPaul Burton clkgr = readl(cgu->base + CGU_REG_CLKGR); 32250d893ffSPaul Burton clkgr &= ~JZ_CLOCK_GATE_TCU; 32350d893ffSPaul Burton clkgr &= ~JZ_CLOCK_GATE_DMAC; 32450d893ffSPaul Burton clkgr &= ~JZ_CLOCK_GATE_UART0; 32550d893ffSPaul Burton writel(clkgr, cgu->base + CGU_REG_CLKGR); 32650d893ffSPaul Burton } 327