2010-07-17 11:10:00 +00:00
/*
* Copyright ( C ) 2010 , Lars - Peter Clausen < lars @ metafoo . de >
* JZ4740 SoC clock support
*
* This program is free software ; you can redistribute it and / or modify it
2013-01-22 12:59:30 +01:00
* under the terms of the GNU General Public License as published by the
2010-07-17 11:10:00 +00:00
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/clk.h>
# include <linux/spinlock.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/list.h>
# include <linux/err.h>
# include <asm/mach-jz4740/clock.h>
# include <asm/mach-jz4740/base.h>
# include "clock.h"
# define JZ_REG_CLOCK_CTRL 0x00
# define JZ_REG_CLOCK_LOW_POWER 0x04
# define JZ_REG_CLOCK_PLL 0x10
# define JZ_REG_CLOCK_GATE 0x20
2013-01-22 12:59:30 +01:00
# define JZ_REG_CLOCK_SLEEP_CTRL 0x24
2010-07-17 11:10:00 +00:00
# define JZ_REG_CLOCK_I2S 0x60
# define JZ_REG_CLOCK_LCD 0x64
# define JZ_REG_CLOCK_MMC 0x68
# define JZ_REG_CLOCK_UHC 0x6C
# define JZ_REG_CLOCK_SPI 0x74
# define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31)
# define JZ_CLOCK_CTRL_KO_ENABLE BIT(30)
# define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29)
# define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000
# define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22)
# define JZ_CLOCK_CTRL_PLL_HALF BIT(21)
# define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000
# define JZ_CLOCK_CTRL_UDIV_OFFSET 23
# define JZ_CLOCK_CTRL_LDIV_OFFSET 16
# define JZ_CLOCK_CTRL_MDIV_OFFSET 12
# define JZ_CLOCK_CTRL_PDIV_OFFSET 8
# define JZ_CLOCK_CTRL_HDIV_OFFSET 4
# define JZ_CLOCK_CTRL_CDIV_OFFSET 0
# define JZ_CLOCK_GATE_UART0 BIT(0)
# define JZ_CLOCK_GATE_TCU BIT(1)
# define JZ_CLOCK_GATE_RTC BIT(2)
# define JZ_CLOCK_GATE_I2C BIT(3)
# define JZ_CLOCK_GATE_SPI BIT(4)
# define JZ_CLOCK_GATE_AIC BIT(5)
# define JZ_CLOCK_GATE_I2S BIT(6)
# define JZ_CLOCK_GATE_MMC BIT(7)
# define JZ_CLOCK_GATE_ADC BIT(8)
# define JZ_CLOCK_GATE_CIM BIT(9)
# define JZ_CLOCK_GATE_LCD BIT(10)
# define JZ_CLOCK_GATE_UDC BIT(11)
# define JZ_CLOCK_GATE_DMAC BIT(12)
# define JZ_CLOCK_GATE_IPU BIT(13)
# define JZ_CLOCK_GATE_UHC BIT(14)
# define JZ_CLOCK_GATE_UART1 BIT(15)
# define JZ_CLOCK_I2S_DIV_MASK 0x01ff
# define JZ_CLOCK_LCD_DIV_MASK 0x01ff
# define JZ_CLOCK_MMC_DIV_MASK 0x001f
# define JZ_CLOCK_UHC_DIV_MASK 0x000f
# define JZ_CLOCK_SPI_SRC_PLL BIT(31)
# define JZ_CLOCK_SPI_DIV_MASK 0x000f
# define JZ_CLOCK_PLL_M_MASK 0x01ff
# define JZ_CLOCK_PLL_N_MASK 0x001f
# define JZ_CLOCK_PLL_OD_MASK 0x0003
# define JZ_CLOCK_PLL_STABLE BIT(10)
# define JZ_CLOCK_PLL_BYPASS BIT(9)
# define JZ_CLOCK_PLL_ENABLED BIT(8)
# define JZ_CLOCK_PLL_STABLIZE_MASK 0x000f
# define JZ_CLOCK_PLL_M_OFFSET 23
# define JZ_CLOCK_PLL_N_OFFSET 18
# define JZ_CLOCK_PLL_OD_OFFSET 16
# define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2)
# define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0)
# define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7)
# define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6)
static void __iomem * jz_clock_base ;
static spinlock_t jz_clock_lock ;
static LIST_HEAD ( jz_clocks ) ;
struct main_clk {
struct clk clk ;
uint32_t div_offset ;
} ;
struct divided_clk {
struct clk clk ;
uint32_t reg ;
uint32_t mask ;
} ;
struct static_clk {
struct clk clk ;
unsigned long rate ;
} ;
static uint32_t jz_clk_reg_read ( int reg )
{
return readl ( jz_clock_base + reg ) ;
}
static void jz_clk_reg_write_mask ( int reg , uint32_t val , uint32_t mask )
{
uint32_t val2 ;
spin_lock ( & jz_clock_lock ) ;
val2 = readl ( jz_clock_base + reg ) ;
val2 & = ~ mask ;
val2 | = val ;
writel ( val2 , jz_clock_base + reg ) ;
spin_unlock ( & jz_clock_lock ) ;
}
static void jz_clk_reg_set_bits ( int reg , uint32_t mask )
{
uint32_t val ;
spin_lock ( & jz_clock_lock ) ;
val = readl ( jz_clock_base + reg ) ;
val | = mask ;
writel ( val , jz_clock_base + reg ) ;
spin_unlock ( & jz_clock_lock ) ;
}
static void jz_clk_reg_clear_bits ( int reg , uint32_t mask )
{
uint32_t val ;
spin_lock ( & jz_clock_lock ) ;
val = readl ( jz_clock_base + reg ) ;
val & = ~ mask ;
writel ( val , jz_clock_base + reg ) ;
spin_unlock ( & jz_clock_lock ) ;
}
static int jz_clk_enable_gating ( struct clk * clk )
{
if ( clk - > gate_bit = = JZ4740_CLK_NOT_GATED )
return - EINVAL ;
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_GATE , clk - > gate_bit ) ;
return 0 ;
}
static int jz_clk_disable_gating ( struct clk * clk )
{
if ( clk - > gate_bit = = JZ4740_CLK_NOT_GATED )
return - EINVAL ;
jz_clk_reg_set_bits ( JZ_REG_CLOCK_GATE , clk - > gate_bit ) ;
return 0 ;
}
static int jz_clk_is_enabled_gating ( struct clk * clk )
{
if ( clk - > gate_bit = = JZ4740_CLK_NOT_GATED )
return 1 ;
return ! ( jz_clk_reg_read ( JZ_REG_CLOCK_GATE ) & clk - > gate_bit ) ;
}
static unsigned long jz_clk_static_get_rate ( struct clk * clk )
{
return ( ( struct static_clk * ) clk ) - > rate ;
}
static int jz_clk_ko_enable ( struct clk * clk )
{
jz_clk_reg_set_bits ( JZ_REG_CLOCK_CTRL , JZ_CLOCK_CTRL_KO_ENABLE ) ;
return 0 ;
}
static int jz_clk_ko_disable ( struct clk * clk )
{
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_CTRL , JZ_CLOCK_CTRL_KO_ENABLE ) ;
return 0 ;
}
static int jz_clk_ko_is_enabled ( struct clk * clk )
{
return ! ! ( jz_clk_reg_read ( JZ_REG_CLOCK_CTRL ) & JZ_CLOCK_CTRL_KO_ENABLE ) ;
}
static const int pllno [ ] = { 1 , 2 , 2 , 4 } ;
static unsigned long jz_clk_pll_get_rate ( struct clk * clk )
{
uint32_t val ;
int m ;
int n ;
int od ;
val = jz_clk_reg_read ( JZ_REG_CLOCK_PLL ) ;
if ( val & JZ_CLOCK_PLL_BYPASS )
return clk_get_rate ( clk - > parent ) ;
m = ( ( val > > 23 ) & 0x1ff ) + 2 ;
n = ( ( val > > 18 ) & 0x1f ) + 2 ;
od = ( val > > 16 ) & 0x3 ;
return ( ( clk_get_rate ( clk - > parent ) / n ) * m ) / pllno [ od ] ;
}
static unsigned long jz_clk_pll_half_get_rate ( struct clk * clk )
{
uint32_t reg ;
reg = jz_clk_reg_read ( JZ_REG_CLOCK_CTRL ) ;
if ( reg & JZ_CLOCK_CTRL_PLL_HALF )
return jz_clk_pll_get_rate ( clk - > parent ) ;
return jz_clk_pll_get_rate ( clk - > parent ) > > 1 ;
}
static const int jz_clk_main_divs [ ] = { 1 , 2 , 3 , 4 , 6 , 8 , 12 , 16 , 24 , 32 } ;
static unsigned long jz_clk_main_round_rate ( struct clk * clk , unsigned long rate )
{
unsigned long parent_rate = jz_clk_pll_get_rate ( clk - > parent ) ;
int div ;
div = parent_rate / rate ;
if ( div > 32 )
return parent_rate / 32 ;
else if ( div < 1 )
return parent_rate ;
div & = ( 0x3 < < ( ffs ( div ) - 1 ) ) ;
return parent_rate / div ;
}
static unsigned long jz_clk_main_get_rate ( struct clk * clk )
{
struct main_clk * mclk = ( struct main_clk * ) clk ;
uint32_t div ;
div = jz_clk_reg_read ( JZ_REG_CLOCK_CTRL ) ;
div > > = mclk - > div_offset ;
div & = 0xf ;
if ( div > = ARRAY_SIZE ( jz_clk_main_divs ) )
div = ARRAY_SIZE ( jz_clk_main_divs ) - 1 ;
return jz_clk_pll_get_rate ( clk - > parent ) / jz_clk_main_divs [ div ] ;
}
static int jz_clk_main_set_rate ( struct clk * clk , unsigned long rate )
{
struct main_clk * mclk = ( struct main_clk * ) clk ;
int i ;
int div ;
unsigned long parent_rate = jz_clk_pll_get_rate ( clk - > parent ) ;
rate = jz_clk_main_round_rate ( clk , rate ) ;
div = parent_rate / rate ;
i = ( ffs ( div ) - 1 ) < < 1 ;
if ( i > 0 & & ! ( div & BIT ( i - 1 ) ) )
i - = 1 ;
jz_clk_reg_write_mask ( JZ_REG_CLOCK_CTRL , i < < mclk - > div_offset ,
0xf < < mclk - > div_offset ) ;
return 0 ;
}
static struct clk_ops jz_clk_static_ops = {
. get_rate = jz_clk_static_get_rate ,
. enable = jz_clk_enable_gating ,
. disable = jz_clk_disable_gating ,
. is_enabled = jz_clk_is_enabled_gating ,
} ;
static struct static_clk jz_clk_ext = {
. clk = {
. name = " ext " ,
. gate_bit = JZ4740_CLK_NOT_GATED ,
. ops = & jz_clk_static_ops ,
} ,
} ;
static struct clk_ops jz_clk_pll_ops = {
. get_rate = jz_clk_pll_get_rate ,
} ;
static struct clk jz_clk_pll = {
. name = " pll " ,
. parent = & jz_clk_ext . clk ,
. ops = & jz_clk_pll_ops ,
} ;
static struct clk_ops jz_clk_pll_half_ops = {
. get_rate = jz_clk_pll_half_get_rate ,
} ;
static struct clk jz_clk_pll_half = {
. name = " pll half " ,
. parent = & jz_clk_pll ,
. ops = & jz_clk_pll_half_ops ,
} ;
static const struct clk_ops jz_clk_main_ops = {
. get_rate = jz_clk_main_get_rate ,
. set_rate = jz_clk_main_set_rate ,
. round_rate = jz_clk_main_round_rate ,
} ;
static struct main_clk jz_clk_cpu = {
. clk = {
. name = " cclk " ,
. parent = & jz_clk_pll ,
. ops = & jz_clk_main_ops ,
} ,
. div_offset = JZ_CLOCK_CTRL_CDIV_OFFSET ,
} ;
static struct main_clk jz_clk_memory = {
. clk = {
. name = " mclk " ,
. parent = & jz_clk_pll ,
. ops = & jz_clk_main_ops ,
} ,
. div_offset = JZ_CLOCK_CTRL_MDIV_OFFSET ,
} ;
static struct main_clk jz_clk_high_speed_peripheral = {
. clk = {
. name = " hclk " ,
. parent = & jz_clk_pll ,
. ops = & jz_clk_main_ops ,
} ,
. div_offset = JZ_CLOCK_CTRL_HDIV_OFFSET ,
} ;
static struct main_clk jz_clk_low_speed_peripheral = {
. clk = {
. name = " pclk " ,
. parent = & jz_clk_pll ,
. ops = & jz_clk_main_ops ,
} ,
. div_offset = JZ_CLOCK_CTRL_PDIV_OFFSET ,
} ;
static const struct clk_ops jz_clk_ko_ops = {
. enable = jz_clk_ko_enable ,
. disable = jz_clk_ko_disable ,
. is_enabled = jz_clk_ko_is_enabled ,
} ;
static struct clk jz_clk_ko = {
. name = " cko " ,
. parent = & jz_clk_memory . clk ,
. ops = & jz_clk_ko_ops ,
} ;
static int jz_clk_spi_set_parent ( struct clk * clk , struct clk * parent )
{
if ( parent = = & jz_clk_pll )
jz_clk_reg_set_bits ( JZ_CLOCK_SPI_SRC_PLL , JZ_REG_CLOCK_SPI ) ;
else if ( parent = = & jz_clk_ext . clk )
jz_clk_reg_clear_bits ( JZ_CLOCK_SPI_SRC_PLL , JZ_REG_CLOCK_SPI ) ;
else
return - EINVAL ;
clk - > parent = parent ;
return 0 ;
}
static int jz_clk_i2s_set_parent ( struct clk * clk , struct clk * parent )
{
if ( parent = = & jz_clk_pll_half )
jz_clk_reg_set_bits ( JZ_REG_CLOCK_CTRL , JZ_CLOCK_CTRL_I2S_SRC_PLL ) ;
else if ( parent = = & jz_clk_ext . clk )
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_CTRL , JZ_CLOCK_CTRL_I2S_SRC_PLL ) ;
else
return - EINVAL ;
clk - > parent = parent ;
return 0 ;
}
static int jz_clk_udc_enable ( struct clk * clk )
{
jz_clk_reg_set_bits ( JZ_REG_CLOCK_SLEEP_CTRL ,
JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC ) ;
return 0 ;
}
static int jz_clk_udc_disable ( struct clk * clk )
{
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_SLEEP_CTRL ,
JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC ) ;
return 0 ;
}
static int jz_clk_udc_is_enabled ( struct clk * clk )
{
return ! ! ( jz_clk_reg_read ( JZ_REG_CLOCK_SLEEP_CTRL ) &
JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC ) ;
}
static int jz_clk_udc_set_parent ( struct clk * clk , struct clk * parent )
{
if ( parent = = & jz_clk_pll_half )
jz_clk_reg_set_bits ( JZ_REG_CLOCK_CTRL , JZ_CLOCK_CTRL_UDC_SRC_PLL ) ;
else if ( parent = = & jz_clk_ext . clk )
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_CTRL , JZ_CLOCK_CTRL_UDC_SRC_PLL ) ;
else
return - EINVAL ;
clk - > parent = parent ;
return 0 ;
}
static int jz_clk_udc_set_rate ( struct clk * clk , unsigned long rate )
{
int div ;
if ( clk - > parent = = & jz_clk_ext . clk )
return - EINVAL ;
div = clk_get_rate ( clk - > parent ) / rate - 1 ;
if ( div < 0 )
div = 0 ;
else if ( div > 63 )
div = 63 ;
jz_clk_reg_write_mask ( JZ_REG_CLOCK_CTRL , div < < JZ_CLOCK_CTRL_UDIV_OFFSET ,
JZ_CLOCK_CTRL_UDIV_MASK ) ;
return 0 ;
}
static unsigned long jz_clk_udc_get_rate ( struct clk * clk )
{
int div ;
if ( clk - > parent = = & jz_clk_ext . clk )
return clk_get_rate ( clk - > parent ) ;
div = ( jz_clk_reg_read ( JZ_REG_CLOCK_CTRL ) & JZ_CLOCK_CTRL_UDIV_MASK ) ;
div > > = JZ_CLOCK_CTRL_UDIV_OFFSET ;
div + = 1 ;
return clk_get_rate ( clk - > parent ) / div ;
}
static unsigned long jz_clk_divided_get_rate ( struct clk * clk )
{
struct divided_clk * dclk = ( struct divided_clk * ) clk ;
int div ;
if ( clk - > parent = = & jz_clk_ext . clk )
return clk_get_rate ( clk - > parent ) ;
div = ( jz_clk_reg_read ( dclk - > reg ) & dclk - > mask ) + 1 ;
return clk_get_rate ( clk - > parent ) / div ;
}
static int jz_clk_divided_set_rate ( struct clk * clk , unsigned long rate )
{
struct divided_clk * dclk = ( struct divided_clk * ) clk ;
int div ;
if ( clk - > parent = = & jz_clk_ext . clk )
return - EINVAL ;
div = clk_get_rate ( clk - > parent ) / rate - 1 ;
if ( div < 0 )
div = 0 ;
else if ( div > dclk - > mask )
div = dclk - > mask ;
jz_clk_reg_write_mask ( dclk - > reg , div , dclk - > mask ) ;
return 0 ;
}
static unsigned long jz_clk_ldclk_round_rate ( struct clk * clk , unsigned long rate )
{
int div ;
unsigned long parent_rate = jz_clk_pll_half_get_rate ( clk - > parent ) ;
if ( rate > 150000000 )
return 150000000 ;
div = parent_rate / rate ;
if ( div < 1 )
div = 1 ;
else if ( div > 32 )
div = 32 ;
return parent_rate / div ;
}
static int jz_clk_ldclk_set_rate ( struct clk * clk , unsigned long rate )
{
int div ;
if ( rate > 150000000 )
return - EINVAL ;
div = jz_clk_pll_half_get_rate ( clk - > parent ) / rate - 1 ;
if ( div < 0 )
div = 0 ;
else if ( div > 31 )
div = 31 ;
jz_clk_reg_write_mask ( JZ_REG_CLOCK_CTRL , div < < JZ_CLOCK_CTRL_LDIV_OFFSET ,
JZ_CLOCK_CTRL_LDIV_MASK ) ;
return 0 ;
}
static unsigned long jz_clk_ldclk_get_rate ( struct clk * clk )
{
int div ;
div = jz_clk_reg_read ( JZ_REG_CLOCK_CTRL ) & JZ_CLOCK_CTRL_LDIV_MASK ;
div > > = JZ_CLOCK_CTRL_LDIV_OFFSET ;
return jz_clk_pll_half_get_rate ( clk - > parent ) / ( div + 1 ) ;
}
static const struct clk_ops jz_clk_ops_ld = {
. set_rate = jz_clk_ldclk_set_rate ,
. get_rate = jz_clk_ldclk_get_rate ,
. round_rate = jz_clk_ldclk_round_rate ,
. enable = jz_clk_enable_gating ,
. disable = jz_clk_disable_gating ,
. is_enabled = jz_clk_is_enabled_gating ,
} ;
static struct clk jz_clk_ld = {
. name = " lcd " ,
. gate_bit = JZ_CLOCK_GATE_LCD ,
. parent = & jz_clk_pll_half ,
. ops = & jz_clk_ops_ld ,
} ;
static const struct clk_ops jz_clk_i2s_ops = {
. set_rate = jz_clk_divided_set_rate ,
. get_rate = jz_clk_divided_get_rate ,
. enable = jz_clk_enable_gating ,
. disable = jz_clk_disable_gating ,
. is_enabled = jz_clk_is_enabled_gating ,
. set_parent = jz_clk_i2s_set_parent ,
} ;
static const struct clk_ops jz_clk_spi_ops = {
. set_rate = jz_clk_divided_set_rate ,
. get_rate = jz_clk_divided_get_rate ,
. enable = jz_clk_enable_gating ,
. disable = jz_clk_disable_gating ,
. is_enabled = jz_clk_is_enabled_gating ,
. set_parent = jz_clk_spi_set_parent ,
} ;
static const struct clk_ops jz_clk_divided_ops = {
. set_rate = jz_clk_divided_set_rate ,
. get_rate = jz_clk_divided_get_rate ,
. enable = jz_clk_enable_gating ,
. disable = jz_clk_disable_gating ,
. is_enabled = jz_clk_is_enabled_gating ,
} ;
static struct divided_clk jz4740_clock_divided_clks [ ] = {
[ 0 ] = {
. clk = {
. name = " i2s " ,
. parent = & jz_clk_ext . clk ,
. gate_bit = JZ_CLOCK_GATE_I2S ,
. ops = & jz_clk_i2s_ops ,
} ,
. reg = JZ_REG_CLOCK_I2S ,
. mask = JZ_CLOCK_I2S_DIV_MASK ,
} ,
[ 1 ] = {
. clk = {
. name = " spi " ,
. parent = & jz_clk_ext . clk ,
. gate_bit = JZ_CLOCK_GATE_SPI ,
. ops = & jz_clk_spi_ops ,
} ,
. reg = JZ_REG_CLOCK_SPI ,
. mask = JZ_CLOCK_SPI_DIV_MASK ,
} ,
[ 2 ] = {
. clk = {
. name = " lcd_pclk " ,
. parent = & jz_clk_pll_half ,
. gate_bit = JZ4740_CLK_NOT_GATED ,
. ops = & jz_clk_divided_ops ,
} ,
. reg = JZ_REG_CLOCK_LCD ,
. mask = JZ_CLOCK_LCD_DIV_MASK ,
} ,
[ 3 ] = {
. clk = {
. name = " mmc " ,
. parent = & jz_clk_pll_half ,
. gate_bit = JZ_CLOCK_GATE_MMC ,
. ops = & jz_clk_divided_ops ,
} ,
. reg = JZ_REG_CLOCK_MMC ,
. mask = JZ_CLOCK_MMC_DIV_MASK ,
} ,
[ 4 ] = {
. clk = {
. name = " uhc " ,
. parent = & jz_clk_pll_half ,
. gate_bit = JZ_CLOCK_GATE_UHC ,
. ops = & jz_clk_divided_ops ,
} ,
. reg = JZ_REG_CLOCK_UHC ,
. mask = JZ_CLOCK_UHC_DIV_MASK ,
} ,
} ;
static const struct clk_ops jz_clk_udc_ops = {
. set_parent = jz_clk_udc_set_parent ,
. set_rate = jz_clk_udc_set_rate ,
. get_rate = jz_clk_udc_get_rate ,
. enable = jz_clk_udc_enable ,
. disable = jz_clk_udc_disable ,
. is_enabled = jz_clk_udc_is_enabled ,
} ;
static const struct clk_ops jz_clk_simple_ops = {
. enable = jz_clk_enable_gating ,
. disable = jz_clk_disable_gating ,
. is_enabled = jz_clk_is_enabled_gating ,
} ;
static struct clk jz4740_clock_simple_clks [ ] = {
[ 0 ] = {
. name = " udc " ,
. parent = & jz_clk_ext . clk ,
. ops = & jz_clk_udc_ops ,
} ,
[ 1 ] = {
. name = " uart0 " ,
. parent = & jz_clk_ext . clk ,
. gate_bit = JZ_CLOCK_GATE_UART0 ,
. ops = & jz_clk_simple_ops ,
} ,
[ 2 ] = {
. name = " uart1 " ,
. parent = & jz_clk_ext . clk ,
. gate_bit = JZ_CLOCK_GATE_UART1 ,
. ops = & jz_clk_simple_ops ,
} ,
[ 3 ] = {
. name = " dma " ,
. parent = & jz_clk_high_speed_peripheral . clk ,
2013-05-30 18:25:00 +02:00
. gate_bit = JZ_CLOCK_GATE_DMAC ,
2010-07-17 11:10:00 +00:00
. ops = & jz_clk_simple_ops ,
} ,
[ 4 ] = {
. name = " ipu " ,
. parent = & jz_clk_high_speed_peripheral . clk ,
. gate_bit = JZ_CLOCK_GATE_IPU ,
. ops = & jz_clk_simple_ops ,
} ,
[ 5 ] = {
. name = " adc " ,
. parent = & jz_clk_ext . clk ,
. gate_bit = JZ_CLOCK_GATE_ADC ,
. ops = & jz_clk_simple_ops ,
} ,
[ 6 ] = {
. name = " i2c " ,
. parent = & jz_clk_ext . clk ,
. gate_bit = JZ_CLOCK_GATE_I2C ,
. ops = & jz_clk_simple_ops ,
} ,
[ 7 ] = {
. name = " aic " ,
. parent = & jz_clk_ext . clk ,
. gate_bit = JZ_CLOCK_GATE_AIC ,
. ops = & jz_clk_simple_ops ,
} ,
} ;
static struct static_clk jz_clk_rtc = {
. clk = {
. name = " rtc " ,
. gate_bit = JZ_CLOCK_GATE_RTC ,
. ops = & jz_clk_static_ops ,
} ,
. rate = 32768 ,
} ;
int clk_enable ( struct clk * clk )
{
if ( ! clk - > ops - > enable )
return - EINVAL ;
return clk - > ops - > enable ( clk ) ;
}
EXPORT_SYMBOL_GPL ( clk_enable ) ;
void clk_disable ( struct clk * clk )
{
if ( clk - > ops - > disable )
clk - > ops - > disable ( clk ) ;
}
EXPORT_SYMBOL_GPL ( clk_disable ) ;
int clk_is_enabled ( struct clk * clk )
{
if ( clk - > ops - > is_enabled )
return clk - > ops - > is_enabled ( clk ) ;
return 1 ;
}
unsigned long clk_get_rate ( struct clk * clk )
{
if ( clk - > ops - > get_rate )
return clk - > ops - > get_rate ( clk ) ;
if ( clk - > parent )
return clk_get_rate ( clk - > parent ) ;
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( clk_get_rate ) ;
int clk_set_rate ( struct clk * clk , unsigned long rate )
{
if ( ! clk - > ops - > set_rate )
return - EINVAL ;
return clk - > ops - > set_rate ( clk , rate ) ;
}
EXPORT_SYMBOL_GPL ( clk_set_rate ) ;
long clk_round_rate ( struct clk * clk , unsigned long rate )
{
if ( clk - > ops - > round_rate )
return clk - > ops - > round_rate ( clk , rate ) ;
return - EINVAL ;
}
EXPORT_SYMBOL_GPL ( clk_round_rate ) ;
int clk_set_parent ( struct clk * clk , struct clk * parent )
{
int ret ;
int enabled ;
if ( ! clk - > ops - > set_parent )
return - EINVAL ;
enabled = clk_is_enabled ( clk ) ;
if ( enabled )
clk_disable ( clk ) ;
ret = clk - > ops - > set_parent ( clk , parent ) ;
if ( enabled )
clk_enable ( clk ) ;
jz4740_clock_debugfs_update_parent ( clk ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( clk_set_parent ) ;
struct clk * clk_get ( struct device * dev , const char * name )
{
struct clk * clk ;
list_for_each_entry ( clk , & jz_clocks , list ) {
if ( strcmp ( clk - > name , name ) = = 0 )
return clk ;
}
return ERR_PTR ( - ENXIO ) ;
}
EXPORT_SYMBOL_GPL ( clk_get ) ;
void clk_put ( struct clk * clk )
{
}
EXPORT_SYMBOL_GPL ( clk_put ) ;
static inline void clk_add ( struct clk * clk )
{
list_add_tail ( & clk - > list , & jz_clocks ) ;
jz4740_clock_debugfs_add_clk ( clk ) ;
}
static void clk_register_clks ( void )
{
size_t i ;
clk_add ( & jz_clk_ext . clk ) ;
clk_add ( & jz_clk_pll ) ;
clk_add ( & jz_clk_pll_half ) ;
clk_add ( & jz_clk_cpu . clk ) ;
clk_add ( & jz_clk_high_speed_peripheral . clk ) ;
clk_add ( & jz_clk_low_speed_peripheral . clk ) ;
clk_add ( & jz_clk_ko ) ;
clk_add ( & jz_clk_ld ) ;
clk_add ( & jz_clk_rtc . clk ) ;
for ( i = 0 ; i < ARRAY_SIZE ( jz4740_clock_divided_clks ) ; + + i )
clk_add ( & jz4740_clock_divided_clks [ i ] . clk ) ;
for ( i = 0 ; i < ARRAY_SIZE ( jz4740_clock_simple_clks ) ; + + i )
clk_add ( & jz4740_clock_simple_clks [ i ] ) ;
}
void jz4740_clock_set_wait_mode ( enum jz4740_wait_mode mode )
{
switch ( mode ) {
case JZ4740_WAIT_MODE_IDLE :
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_LOW_POWER , JZ_CLOCK_LOW_POWER_MODE_SLEEP ) ;
break ;
case JZ4740_WAIT_MODE_SLEEP :
jz_clk_reg_set_bits ( JZ_REG_CLOCK_LOW_POWER , JZ_CLOCK_LOW_POWER_MODE_SLEEP ) ;
break ;
}
}
void jz4740_clock_udc_disable_auto_suspend ( void )
{
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_GATE , JZ_CLOCK_GATE_UDC ) ;
}
EXPORT_SYMBOL_GPL ( jz4740_clock_udc_disable_auto_suspend ) ;
void jz4740_clock_udc_enable_auto_suspend ( void )
{
jz_clk_reg_set_bits ( JZ_REG_CLOCK_GATE , JZ_CLOCK_GATE_UDC ) ;
}
EXPORT_SYMBOL_GPL ( jz4740_clock_udc_enable_auto_suspend ) ;
void jz4740_clock_suspend ( void )
{
jz_clk_reg_set_bits ( JZ_REG_CLOCK_GATE ,
JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0 ) ;
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_PLL , JZ_CLOCK_PLL_ENABLED ) ;
}
void jz4740_clock_resume ( void )
{
uint32_t pll ;
jz_clk_reg_set_bits ( JZ_REG_CLOCK_PLL , JZ_CLOCK_PLL_ENABLED ) ;
do {
pll = jz_clk_reg_read ( JZ_REG_CLOCK_PLL ) ;
} while ( ! ( pll & JZ_CLOCK_PLL_STABLE ) ) ;
jz_clk_reg_clear_bits ( JZ_REG_CLOCK_GATE ,
JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0 ) ;
}
static int jz4740_clock_init ( void )
{
uint32_t val ;
jz_clock_base = ioremap ( JZ4740_CPM_BASE_ADDR , 0x100 ) ;
if ( ! jz_clock_base )
return - EBUSY ;
spin_lock_init ( & jz_clock_lock ) ;
jz_clk_ext . rate = jz4740_clock_bdata . ext_rate ;
jz_clk_rtc . rate = jz4740_clock_bdata . rtc_rate ;
val = jz_clk_reg_read ( JZ_REG_CLOCK_SPI ) ;
if ( val & JZ_CLOCK_SPI_SRC_PLL )
jz4740_clock_divided_clks [ 1 ] . clk . parent = & jz_clk_pll_half ;
val = jz_clk_reg_read ( JZ_REG_CLOCK_CTRL ) ;
if ( val & JZ_CLOCK_CTRL_I2S_SRC_PLL )
jz4740_clock_divided_clks [ 0 ] . clk . parent = & jz_clk_pll_half ;
if ( val & JZ_CLOCK_CTRL_UDC_SRC_PLL )
jz4740_clock_simple_clks [ 0 ] . parent = & jz_clk_pll_half ;
jz4740_clock_debugfs_init ( ) ;
clk_register_clks ( ) ;
return 0 ;
}
arch_initcall ( jz4740_clock_init ) ;