2015-05-24 16:11:36 +01:00
/*
* Ingenic JZ4740 SoC CGU driver
*
* Copyright ( c ) 2015 Imagination Technologies
2017-10-25 17:04:33 -07:00
* Author : Paul Burton < paul . burton @ mips . com >
2015-05-24 16:11:36 +01:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/clk-provider.h>
# include <linux/delay.h>
# include <linux/of.h>
# include <dt-bindings/clock/jz4740-cgu.h>
2015-05-24 16:11:37 +01:00
# include <asm/mach-jz4740/clock.h>
2015-05-24 16:11:36 +01:00
# include "cgu.h"
/* CGU register offsets */
# define CGU_REG_CPCCR 0x00
2015-05-24 16:11:37 +01:00
# define CGU_REG_LCR 0x04
2015-05-24 16:11:36 +01:00
# define CGU_REG_CPPCR 0x10
2015-05-24 16:11:38 +01:00
# define CGU_REG_CLKGR 0x20
2015-05-24 16:11:36 +01:00
# define CGU_REG_SCR 0x24
# define CGU_REG_I2SCDR 0x60
# define CGU_REG_LPCDR 0x64
# define CGU_REG_MSCCDR 0x68
# define CGU_REG_UHCCDR 0x6c
# define CGU_REG_SSICDR 0x74
/* bits within a PLL control register */
# define PLLCTL_M_SHIFT 23
# define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT)
# define PLLCTL_N_SHIFT 18
# define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT)
# define PLLCTL_OD_SHIFT 16
# define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT)
# define PLLCTL_STABLE (1 << 10)
# define PLLCTL_BYPASS (1 << 9)
# define PLLCTL_ENABLE (1 << 8)
2015-05-24 16:11:37 +01:00
/* bits within the LCR register */
# define LCR_SLEEP (1 << 0)
2015-05-24 16:11:38 +01:00
/* bits within the CLKGR register */
# define CLKGR_UDC (1 << 11)
2015-05-24 16:11:36 +01:00
static struct ingenic_cgu * cgu ;
static const s8 pll_od_encoding [ 4 ] = {
0x0 , 0x1 , - 1 , 0x3 ,
} ;
static const struct ingenic_cgu_clk_info jz4740_cgu_clocks [ ] = {
/* External clocks */
[ JZ4740_CLK_EXT ] = { " ext " , CGU_CLK_EXT } ,
[ JZ4740_CLK_RTC ] = { " rtc " , CGU_CLK_EXT } ,
[ JZ4740_CLK_PLL ] = {
" pll " , CGU_CLK_PLL ,
. parents = { JZ4740_CLK_EXT , - 1 , - 1 , - 1 } ,
. pll = {
. reg = CGU_REG_CPPCR ,
. m_shift = 23 ,
. m_bits = 9 ,
. m_offset = 2 ,
. n_shift = 18 ,
. n_bits = 5 ,
. n_offset = 2 ,
. od_shift = 16 ,
. od_bits = 2 ,
. od_max = 4 ,
. od_encoding = pll_od_encoding ,
. stable_bit = 10 ,
. bypass_bit = 9 ,
. enable_bit = 8 ,
} ,
} ,
/* Muxes & dividers */
[ JZ4740_CLK_PLL_HALF ] = {
" pll half " , CGU_CLK_DIV ,
. parents = { JZ4740_CLK_PLL , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_CPCCR , 21 , 1 , 1 , - 1 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
} ,
[ JZ4740_CLK_CCLK ] = {
" cclk " , CGU_CLK_DIV ,
. parents = { JZ4740_CLK_PLL , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_CPCCR , 0 , 1 , 4 , 22 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
} ,
[ JZ4740_CLK_HCLK ] = {
" hclk " , CGU_CLK_DIV ,
. parents = { JZ4740_CLK_PLL , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_CPCCR , 4 , 1 , 4 , 22 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
} ,
[ JZ4740_CLK_PCLK ] = {
" pclk " , CGU_CLK_DIV ,
. parents = { JZ4740_CLK_PLL , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_CPCCR , 8 , 1 , 4 , 22 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
} ,
[ JZ4740_CLK_MCLK ] = {
" mclk " , CGU_CLK_DIV ,
. parents = { JZ4740_CLK_PLL , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_CPCCR , 12 , 1 , 4 , 22 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
} ,
[ JZ4740_CLK_LCD ] = {
" lcd " , CGU_CLK_DIV | CGU_CLK_GATE ,
. parents = { JZ4740_CLK_PLL_HALF , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_CPCCR , 16 , 1 , 5 , 22 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
. gate = { CGU_REG_CLKGR , 10 } ,
} ,
[ JZ4740_CLK_LCD_PCLK ] = {
" lcd_pclk " , CGU_CLK_DIV ,
. parents = { JZ4740_CLK_PLL_HALF , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_LPCDR , 0 , 1 , 11 , - 1 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
} ,
[ JZ4740_CLK_I2S ] = {
" i2s " , CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE ,
. parents = { JZ4740_CLK_EXT , JZ4740_CLK_PLL_HALF , - 1 , - 1 } ,
. mux = { CGU_REG_CPCCR , 31 , 1 } ,
2018-06-27 14:14:58 +02:00
. div = { CGU_REG_I2SCDR , 0 , 1 , 9 , - 1 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
. gate = { CGU_REG_CLKGR , 6 } ,
} ,
[ JZ4740_CLK_SPI ] = {
" spi " , CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE ,
. parents = { JZ4740_CLK_EXT , JZ4740_CLK_PLL , - 1 , - 1 } ,
. mux = { CGU_REG_SSICDR , 31 , 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_SSICDR , 0 , 1 , 4 , - 1 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
. gate = { CGU_REG_CLKGR , 4 } ,
} ,
[ JZ4740_CLK_MMC ] = {
" mmc " , CGU_CLK_DIV | CGU_CLK_GATE ,
. parents = { JZ4740_CLK_PLL_HALF , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_MSCCDR , 0 , 1 , 5 , - 1 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
. gate = { CGU_REG_CLKGR , 7 } ,
} ,
[ JZ4740_CLK_UHC ] = {
" uhc " , CGU_CLK_DIV | CGU_CLK_GATE ,
. parents = { JZ4740_CLK_PLL_HALF , - 1 , - 1 , - 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_UHCCDR , 0 , 1 , 4 , - 1 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
. gate = { CGU_REG_CLKGR , 14 } ,
} ,
[ JZ4740_CLK_UDC ] = {
2018-06-27 14:14:59 +02:00
" udc " , CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE ,
2015-05-24 16:11:36 +01:00
. parents = { JZ4740_CLK_EXT , JZ4740_CLK_PLL_HALF , - 1 , - 1 } ,
. mux = { CGU_REG_CPCCR , 29 , 1 } ,
2016-05-09 17:29:52 +01:00
. div = { CGU_REG_CPCCR , 23 , 1 , 6 , - 1 , - 1 , - 1 } ,
2015-05-24 16:11:36 +01:00
. gate = { CGU_REG_SCR , 6 } ,
} ,
/* Gate-only clocks */
[ JZ4740_CLK_UART0 ] = {
" uart0 " , CGU_CLK_GATE ,
. parents = { JZ4740_CLK_EXT , - 1 , - 1 , - 1 } ,
. gate = { CGU_REG_CLKGR , 0 } ,
} ,
[ JZ4740_CLK_UART1 ] = {
" uart1 " , CGU_CLK_GATE ,
. parents = { JZ4740_CLK_EXT , - 1 , - 1 , - 1 } ,
. gate = { CGU_REG_CLKGR , 15 } ,
} ,
[ JZ4740_CLK_DMA ] = {
" dma " , CGU_CLK_GATE ,
. parents = { JZ4740_CLK_PCLK , - 1 , - 1 , - 1 } ,
. gate = { CGU_REG_CLKGR , 12 } ,
} ,
[ JZ4740_CLK_IPU ] = {
" ipu " , CGU_CLK_GATE ,
. parents = { JZ4740_CLK_PCLK , - 1 , - 1 , - 1 } ,
. gate = { CGU_REG_CLKGR , 13 } ,
} ,
[ JZ4740_CLK_ADC ] = {
" adc " , CGU_CLK_GATE ,
. parents = { JZ4740_CLK_EXT , - 1 , - 1 , - 1 } ,
. gate = { CGU_REG_CLKGR , 8 } ,
} ,
[ JZ4740_CLK_I2C ] = {
" i2c " , CGU_CLK_GATE ,
. parents = { JZ4740_CLK_EXT , - 1 , - 1 , - 1 } ,
. gate = { CGU_REG_CLKGR , 3 } ,
} ,
[ JZ4740_CLK_AIC ] = {
" aic " , CGU_CLK_GATE ,
. parents = { JZ4740_CLK_EXT , - 1 , - 1 , - 1 } ,
. gate = { CGU_REG_CLKGR , 5 } ,
} ,
} ;
static void __init jz4740_cgu_init ( struct device_node * np )
{
int retval ;
cgu = ingenic_cgu_new ( jz4740_cgu_clocks ,
ARRAY_SIZE ( jz4740_cgu_clocks ) , np ) ;
if ( ! cgu ) {
pr_err ( " %s: failed to initialise CGU \n " , __func__ ) ;
return ;
}
retval = ingenic_cgu_register_clocks ( cgu ) ;
if ( retval )
pr_err ( " %s: failed to register CGU Clocks \n " , __func__ ) ;
}
CLK_OF_DECLARE ( jz4740_cgu , " ingenic,jz4740-cgu " , jz4740_cgu_init ) ;
2015-05-24 16:11:37 +01:00
void jz4740_clock_set_wait_mode ( enum jz4740_wait_mode mode )
{
uint32_t lcr = readl ( cgu - > base + CGU_REG_LCR ) ;
switch ( mode ) {
case JZ4740_WAIT_MODE_IDLE :
lcr & = ~ LCR_SLEEP ;
break ;
case JZ4740_WAIT_MODE_SLEEP :
lcr | = LCR_SLEEP ;
break ;
}
writel ( lcr , cgu - > base + CGU_REG_LCR ) ;
}
2015-05-24 16:11:38 +01:00
void jz4740_clock_udc_disable_auto_suspend ( void )
{
uint32_t clkgr = readl ( cgu - > base + CGU_REG_CLKGR ) ;
clkgr & = ~ CLKGR_UDC ;
writel ( clkgr , cgu - > base + CGU_REG_CLKGR ) ;
}
EXPORT_SYMBOL_GPL ( jz4740_clock_udc_disable_auto_suspend ) ;
void jz4740_clock_udc_enable_auto_suspend ( void )
{
uint32_t clkgr = readl ( cgu - > base + CGU_REG_CLKGR ) ;
clkgr | = CLKGR_UDC ;
writel ( clkgr , cgu - > base + CGU_REG_CLKGR ) ;
}
EXPORT_SYMBOL_GPL ( jz4740_clock_udc_enable_auto_suspend ) ;
2015-05-24 16:11:39 +01:00
# define JZ_CLOCK_GATE_UART0 BIT(0)
# define JZ_CLOCK_GATE_TCU BIT(1)
# define JZ_CLOCK_GATE_DMAC BIT(12)
void jz4740_clock_suspend ( void )
{
uint32_t clkgr , cppcr ;
clkgr = readl ( cgu - > base + CGU_REG_CLKGR ) ;
clkgr | = JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0 ;
writel ( clkgr , cgu - > base + CGU_REG_CLKGR ) ;
cppcr = readl ( cgu - > base + CGU_REG_CPPCR ) ;
cppcr & = ~ BIT ( jz4740_cgu_clocks [ JZ4740_CLK_PLL ] . pll . enable_bit ) ;
writel ( cppcr , cgu - > base + CGU_REG_CPPCR ) ;
}
void jz4740_clock_resume ( void )
{
uint32_t clkgr , cppcr , stable ;
cppcr = readl ( cgu - > base + CGU_REG_CPPCR ) ;
cppcr | = BIT ( jz4740_cgu_clocks [ JZ4740_CLK_PLL ] . pll . enable_bit ) ;
writel ( cppcr , cgu - > base + CGU_REG_CPPCR ) ;
stable = BIT ( jz4740_cgu_clocks [ JZ4740_CLK_PLL ] . pll . stable_bit ) ;
do {
cppcr = readl ( cgu - > base + CGU_REG_CPPCR ) ;
} while ( ! ( cppcr & stable ) ) ;
clkgr = readl ( cgu - > base + CGU_REG_CLKGR ) ;
clkgr & = ~ JZ_CLOCK_GATE_TCU ;
clkgr & = ~ JZ_CLOCK_GATE_DMAC ;
clkgr & = ~ JZ_CLOCK_GATE_UART0 ;
writel ( clkgr , cgu - > base + CGU_REG_CLKGR ) ;
}