From b8419470a2ed68f0f5ad824b8c87489efee5a7eb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 2 Sep 2009 11:15:26 +0200 Subject: [PATCH] Add clock support. Mostly untested. --- .../arch/mips/include/asm/mach-jz4740/clock.h | 2 + .../files-2.6.31/arch/mips/jz4740/Makefile | 3 +- .../files-2.6.31/arch/mips/jz4740/clock.c | 777 ++++++++++++++++++ 3 files changed, 781 insertions(+), 1 deletion(-) create mode 100644 target/linux/xburst/files-2.6.31/arch/mips/jz4740/clock.c diff --git a/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/clock.h b/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/clock.h index 11ffe8894..0f7a35064 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/clock.h +++ b/target/linux/xburst/files-2.6.31/arch/mips/include/asm/mach-jz4740/clock.h @@ -170,4 +170,6 @@ static inline void __cpm_select_msc_clk(int sd) REG_CPM_MSCCDR = div - 1; } +int jz_init_clocks(unsigned long ext_rate); + #endif /* __ASM_JZ4740_CLOCK_H__ */ diff --git a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/Makefile b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/Makefile index 02598762b..f22c8d13d 100644 --- a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/Makefile +++ b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/Makefile @@ -5,7 +5,7 @@ # Object file lists. obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ - gpio.o platform.o i2c.o + gpio.o clock.o platform.o i2c.o obj-$(CONFIG_PROC_FS) += proc.o @@ -16,6 +16,7 @@ obj-$(CONFIG_JZ4740_LEO) += board-leo.o obj-$(CONFIG_JZ4740_LYRA) += board-lyra.o obj-$(CONFIG_JZ4725_DIPPER) += board-dipper.o obj-$(CONFIG_JZ4720_VIRGO) += board-virgo.o +obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o # PM support diff --git a/target/linux/xburst/files-2.6.31/arch/mips/jz4740/clock.c b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/clock.c new file mode 100644 index 000000000..7b43bf231 --- /dev/null +++ b/target/linux/xburst/files-2.6.31/arch/mips/jz4740/clock.c @@ -0,0 +1,777 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define JZ_REG_CLOCK_CTRL 0x00 +#define JZ_REG_CLOCK_PLL 0x10 +#define JZ_REG_CLOCK_GATE 0x20 +#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_PCLK BIT(5) +#define JZ_CLOCK_GATE_AIC 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 + +static void __iomem *jz_clock_base; +spinlock_t jz_clock_lock; +static LIST_HEAD(jz_clocks); + +struct clk { + const char *name; + struct clk* parent; + + uint32_t gate_bit; + + unsigned long (*get_rate)(struct clk* clk); + unsigned long (*round_rate)(struct clk *clk, unsigned long rate); + int (*set_rate)(struct clk* clk, unsigned long rate); + int (*enable)(struct clk* clk); + int (*disable)(struct clk* clk); + + int (*set_parent)(struct clk* clk, struct clk *parent); + struct list_head list; +}; + +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) +{ + jz_clk_reg_clear_bits(JZ_REG_CLOCK_GATE, clk->gate_bit); + return 0; +} + +static int jz_clk_disable_gating(struct clk *clk) +{ + jz_clk_reg_set_bits(JZ_REG_CLOCK_GATE, clk->gate_bit); + return 0; +} + +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 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) * (m / n) / 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(NULL) >> 1; + return jz_clk_pll_get_rate(NULL); +} + + + +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(NULL); + 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(NULL) / 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(NULL); + + 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 static_clk jz_clk_ext = { + .clk = { + .name = "ext", + .get_rate = jz_clk_static_get_rate, + }, +}; + +static struct clk jz_clk_pll = { + .name = "pll", + .parent = &jz_clk_ext.clk, + .get_rate = jz_clk_pll_get_rate, +}; + +static struct clk jz_clk_pll_half = { + .name = "pll half", + .parent = &jz_clk_pll, + .get_rate = jz_clk_pll_half_get_rate, +}; + +static struct main_clk jz_clk_cpu = { + .clk = { + .name = "cclk", + .parent = &jz_clk_pll, + .get_rate = jz_clk_main_get_rate, + .set_rate = jz_clk_main_set_rate, + .round_rate = jz_clk_main_round_rate, + }, + .div_offset = JZ_CLOCK_CTRL_CDIV_OFFSET, +}; + +static struct main_clk jz_clk_memory = { + .clk = { + .name = "mclk", + .parent = &jz_clk_pll, + .get_rate = jz_clk_main_get_rate, + .set_rate = jz_clk_main_set_rate, + .round_rate = jz_clk_main_round_rate, + }, + .div_offset = JZ_CLOCK_CTRL_MDIV_OFFSET, +}; + +static struct main_clk jz_clk_high_speed_peripheral = { + .clk = { + .name = "hclk", + .parent = &jz_clk_pll, + .get_rate = jz_clk_main_get_rate, + .set_rate = jz_clk_main_set_rate, + .round_rate = jz_clk_main_round_rate, + }, + .div_offset = JZ_CLOCK_CTRL_HDIV_OFFSET, +}; + + +static struct main_clk jz_clk_low_speed_peripheral = { + .clk = { + .name = "pclk", + .parent = &jz_clk_pll, + .get_rate = jz_clk_main_get_rate, + .set_rate = jz_clk_main_set_rate, + }, + .div_offset = JZ_CLOCK_CTRL_PDIV_OFFSET, +}; + +static struct clk jz_clk_ko = { + .name = "cko", + .parent = &jz_clk_memory.clk, + .enable = jz_clk_ko_enable, + .disable = jz_clk_ko_disable, +}; + +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_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(NULL); + + 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(NULL) / 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); +} + +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(NULL) / (div + 1); +} + +static struct clk jz_clk_ld = { + .name = "lcd", + .parent = &jz_clk_pll_half, + .set_rate = jz_clk_ldclk_set_rate, + .get_rate = jz_clk_ldclk_get_rate, + .round_rate = jz_clk_ldclk_round_rate, +}; + +static struct divided_clk jz_clk_lp = { + .clk = { + .name = "lcd_pclk", + .parent = &jz_clk_pll_half, + }, + .reg = JZ_REG_CLOCK_LCD, + .mask = JZ_CLOCK_LCD_DIV_MASK, +}; + +static struct clk jz_clk_cim_mclk = { + .name = "cim_mclk", + .parent = &jz_clk_high_speed_peripheral.clk, +}; + +static struct static_clk jz_clk_cim_pclk = { + .clk = { + .name = "cim_pclk", + .gate_bit = JZ_CLOCK_GATE_CIM, + .get_rate = jz_clk_static_get_rate, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, + }, +}; + +static struct divided_clk jz_clk_i2s = { + .clk = { + .name = "i2s", + .parent = &jz_clk_ext.clk, + .gate_bit = JZ_CLOCK_GATE_AIC, + .set_parent = jz_clk_i2s_set_parent, + .set_rate = jz_clk_divided_set_rate, + .get_rate = jz_clk_divided_get_rate, + }, + .reg = JZ_REG_CLOCK_I2S, + .mask = JZ_CLOCK_I2S_DIV_MASK, +}; + +static struct divided_clk jz_clk_mmc = { + .clk = { + .name = "mmc", + .parent = &jz_clk_pll_half, + .gate_bit = JZ_CLOCK_GATE_MMC, + .set_rate = jz_clk_divided_set_rate, + .get_rate = jz_clk_divided_get_rate, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, + }, + .reg = JZ_REG_CLOCK_MMC, + .mask = JZ_CLOCK_MMC_DIV_MASK, +}; + +static struct divided_clk jz_clk_uhc = { + .clk = { + .name = "uhc", + .parent = &jz_clk_pll_half, + .gate_bit = JZ_CLOCK_GATE_UHC, + .set_rate = jz_clk_divided_set_rate, + .get_rate = jz_clk_divided_get_rate, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, + }, + .reg = JZ_REG_CLOCK_UHC, + .mask = JZ_CLOCK_UHC_DIV_MASK, +}; + +static struct clk jz_clk_udc = { + .name = "udc", + .parent = &jz_clk_ext.clk, + .set_parent = jz_clk_udc_set_parent, + .set_rate = jz_clk_udc_set_rate, + .get_rate = jz_clk_udc_get_rate, +}; + +static struct divided_clk jz_clk_spi = { + .clk = { + .name = "spi", + .parent = &jz_clk_ext.clk, + .gate_bit = JZ_CLOCK_GATE_SPI, + .set_rate = jz_clk_divided_set_rate, + .get_rate = jz_clk_divided_get_rate, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, + .set_parent = jz_clk_spi_set_parent, + }, + .reg = JZ_REG_CLOCK_SPI, + .mask = JZ_CLOCK_SPI_DIV_MASK, +}; + +static struct clk jz_clk_uart0 = { + .name = "uart0", + .parent = &jz_clk_ext.clk, + .gate_bit = JZ_CLOCK_GATE_UART0, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, +}; + +static struct clk jz_clk_uart1 = { + .name = "uart1", + .parent = &jz_clk_ext.clk, + .gate_bit = JZ_CLOCK_GATE_UART1, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, +}; + +static struct clk jz_clk_dma = { + .name = "dma", + .parent = &jz_clk_high_speed_peripheral.clk, + .gate_bit = JZ_CLOCK_GATE_UART0, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, +}; + +static struct clk jz_clk_ipu = { + .name = "ipu", + .parent = &jz_clk_high_speed_peripheral.clk, + .gate_bit = JZ_CLOCK_GATE_IPU, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, +}; + +static struct clk jz_clk_adc = { + .name = "adc", + .parent = &jz_clk_ext.clk, + .gate_bit = JZ_CLOCK_GATE_ADC, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, +}; + +static struct clk jz_clk_i2c = { + .name = "i2c", + .parent = &jz_clk_ext.clk, + .gate_bit = JZ_CLOCK_GATE_I2C, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, +}; + +static struct static_clk jz_clk_rtc = { + .clk = { + .name = "rtc", + .gate_bit = JZ_CLOCK_GATE_RTC, + .enable = jz_clk_enable_gating, + .disable = jz_clk_disable_gating, + }, + .rate = 32768, +}; + +int clk_enable(struct clk *clk) +{ + if (!clk->enable) + return -EINVAL; + + return clk->enable(clk); +} +EXPORT_SYMBOL_GPL(clk_enable); + +void clk_disable(struct clk *clk) +{ + if (clk->disable) + clk->disable(clk); +} +EXPORT_SYMBOL_GPL(clk_disable); + +unsigned long clk_get_rate(struct clk *clk) +{ + if (clk->get_rate) + return clk->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->set_rate) + return -EINVAL; + return clk->set_rate(clk, rate); +} +EXPORT_SYMBOL_GPL(clk_set_rate); + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (clk->round_rate) + return clk->round_rate(clk, rate); + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(clk_round_rate); + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret; + + if (!clk->set_parent) + return -EINVAL; + + clk->disable(clk); + ret = clk->set_parent(clk, parent); + clk->enable(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)) + return clk; + } + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL_GPL(clk_get); + +void clk_put(struct clk *clk) +{ +} +EXPORT_SYMBOL_GPL(clk_put); + +inline static void clk_add(struct clk *clk) +{ + list_add_tail(&clk->list, &jz_clocks); +} + +static void clk_register_clks(void) +{ + 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_lp.clk); + clk_add(&jz_clk_cim_mclk); + clk_add(&jz_clk_cim_pclk.clk); + clk_add(&jz_clk_i2s.clk); + clk_add(&jz_clk_mmc.clk); + clk_add(&jz_clk_uhc.clk); + clk_add(&jz_clk_udc); + clk_add(&jz_clk_uart0); + clk_add(&jz_clk_uart1); + clk_add(&jz_clk_dma); + clk_add(&jz_clk_ipu); + clk_add(&jz_clk_adc); + clk_add(&jz_clk_i2c); + clk_add(&jz_clk_rtc.clk); +} + +int jz_init_clocks(unsigned long ext_rate) +{ + uint32_t val; + + jz_clock_base = ioremap(0x10000000, 0x100); + if (!jz_clock_base) + return -EBUSY; + + jz_clk_ext.rate = ext_rate; + + val = jz_clk_reg_read(JZ_REG_CLOCK_SPI); + + if (val & JZ_CLOCK_SPI_SRC_PLL) + jz_clk_spi.clk.parent = &jz_clk_pll_half; + + val = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); + + if (val & JZ_CLOCK_CTRL_I2S_SRC_PLL) + jz_clk_i2s.clk.parent = &jz_clk_pll_half; + + if (val & JZ_CLOCK_CTRL_UDC_SRC_PLL) + jz_clk_udc.parent = &jz_clk_pll_half; + + clk_register_clks(); + + return 0; +} +EXPORT_SYMBOL_GPL(jz_init_clocks);