diff options
4 files changed, 255 insertions, 51 deletions
diff --git a/target/linux/xburst/files-2.6.32/arch/mips/jz4740/Makefile b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/Makefile index 19fc95ce55..b61f5cf8cc 100644 --- a/target/linux/xburst/files-2.6.32/arch/mips/jz4740/Makefile +++ b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/Makefile @@ -7,6 +7,8 @@ obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ gpio.o clock.o platform.o +obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o + # board specific support obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o diff --git a/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock-debugfs.c b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock-debugfs.c new file mode 100644 index 0000000000..8968f7c67f --- /dev/null +++ b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock-debugfs.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> + * JZ4740 SoC clock support debugfs entries + * + * 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. + * + * 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 02139, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <linux/debugfs.h> +#include <linux/uaccess.h> + +#include <asm/mach-jz4740/clock.h> +#include "clock.h" + +static struct dentry *jz4740_clock_debugfs; + +static int jz4740_clock_debugfs_show_enabled(void *data, uint64_t *value) +{ + struct clk *clk = data; + *value = clk_is_enabled(clk); + + return 0; +} + +static int jz4740_clock_debugfs_set_enabled(void *data, uint64_t value) +{ + struct clk *clk = data; + + if (value) + return clk_enable(clk); + else + clk_disable(clk); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(jz4740_clock_debugfs_ops_enabled, + jz4740_clock_debugfs_show_enabled, + jz4740_clock_debugfs_set_enabled, + "%llu\n"); + +static int jz4740_clock_debugfs_show_rate(void *data, uint64_t *value) +{ + struct clk *clk = data; + *value = clk_get_rate(clk); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(jz4740_clock_debugfs_ops_rate, + jz4740_clock_debugfs_show_rate, + NULL, + "%llu\n"); + +void jz4740_clock_debugfs_add_clk(struct clk *clk) +{ + if (!jz4740_clock_debugfs) + return; + + clk->debugfs_entry = debugfs_create_dir(clk->name, jz4740_clock_debugfs); + debugfs_create_file("rate", S_IWUGO | S_IRUGO, clk->debugfs_entry, clk, + &jz4740_clock_debugfs_ops_rate); + debugfs_create_file("enabled", S_IRUGO, clk->debugfs_entry, clk, + &jz4740_clock_debugfs_ops_enabled); + + if (clk->parent) { + char parent_path[100]; + snprintf(parent_path, 100, "../%s", clk->parent->name); + clk->debugfs_parent_entry = debugfs_create_symlink("parent", + clk->debugfs_entry, + parent_path); + } +} + +/* TODO: Locking */ +void jz4740_clock_debugfs_update_parent(struct clk *clk) +{ + if (clk->debugfs_parent_entry) + debugfs_remove(clk->debugfs_parent_entry); + + if (clk->parent) { + char parent_path[100]; + snprintf(parent_path, 100, "../%s", clk->parent->name); + clk->debugfs_parent_entry = debugfs_create_symlink("parent", + clk->debugfs_entry, + parent_path); + } else { + clk->debugfs_parent_entry = NULL; + } +} + +void jz4740_clock_debugfs_init(void) +{ + jz4740_clock_debugfs = debugfs_create_dir("jz4740-clock", NULL); + if (IS_ERR(jz4740_clock_debugfs)) + jz4740_clock_debugfs = NULL; +} diff --git a/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock.c b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock.c index 49ea7171a3..1145921bac 100644 --- a/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock.c +++ b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock.c @@ -23,6 +23,7 @@ #include <linux/err.h> #include <asm/mach-jz4740/clock.h> +#include "clock.h" #define JZ_REG_CLOCK_CTRL 0x00 #define JZ_REG_CLOCK_LOW_POWER 0x04 @@ -98,27 +99,6 @@ static void __iomem *jz_clock_base; static spinlock_t jz_clock_lock; static LIST_HEAD(jz_clocks); -struct clk_ops { - 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 clk { - const char *name; - struct clk* parent; - - uint32_t gate_bit; - - const struct clk_ops *ops; - - struct list_head list; -}; - struct main_clk { struct clk clk; uint32_t div_offset; @@ -176,33 +156,51 @@ static void jz_clk_reg_clear_bits(int reg, uint32_t mask) 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) +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) +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}; @@ -293,12 +291,13 @@ 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 = (uint32_t)-1, + .gate_bit = JZ4740_CLK_NOT_GATED, .ops = &jz_clk_static_ops, }, }; @@ -369,6 +368,7 @@ static struct main_clk jz_clk_low_speed_peripheral = { 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 = { @@ -405,22 +405,27 @@ static int jz_clk_i2s_set_parent(struct clk *clk, struct clk *parent) return 0; } -static int jz_clk_udc_disable(struct clk *clk) +static int jz_clk_udc_enable(struct clk *clk) { - jz_clk_reg_clear_bits(JZ_REG_CLOCK_SLEEP_CTRL, + jz_clk_reg_set_bits(JZ_REG_CLOCK_SLEEP_CTRL, JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC); return 0; } -static int jz_clk_udc_enable(struct clk *clk) +static int jz_clk_udc_disable(struct clk *clk) { - jz_clk_reg_set_bits(JZ_REG_CLOCK_SLEEP_CTRL, + 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) @@ -551,23 +556,19 @@ 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 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, + .ops = &jz_clk_ops_ld, }; +/* TODO: ops!!! */ static struct clk jz_clk_cim_mclk = { .name = "cim_mclk", .parent = &jz_clk_high_speed_peripheral.clk, @@ -587,6 +588,7 @@ static const struct clk_ops jz_clk_i2s_ops = .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, }; @@ -596,6 +598,7 @@ static const struct clk_ops jz_clk_spi_ops = .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, }; @@ -605,11 +608,22 @@ static const struct clk_ops jz_clk_divided_ops = .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[] = { { .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, + }, + { + .clk = { .name = "i2s", .parent = &jz_clk_ext.clk, .gate_bit = JZ_CLOCK_GATE_AIC, @@ -656,11 +670,13 @@ static const struct clk_ops jz_clk_udc_ops = { .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[] = { @@ -732,6 +748,14 @@ void clk_disable(struct clk *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) @@ -771,6 +795,8 @@ int clk_set_parent(struct clk *clk, struct clk *parent) ret = clk->ops->set_parent(clk, parent); clk_enable(clk); + jz4740_clock_debugfs_update_parent(clk); + return ret; } EXPORT_SYMBOL_GPL(clk_set_parent); @@ -792,27 +818,29 @@ 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); + 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_lp.clk); - clk_add(&jz_clk_cim_mclk); - clk_add(&jz_clk_cim_pclk.clk); - clk_add(&jz_clk_rtc.clk); + 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_cim_mclk); + clk_add(&jz_clk_cim_pclk.clk); + 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); @@ -870,8 +898,11 @@ int jz_init_clocks(unsigned long ext_rate) 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; } EXPORT_SYMBOL_GPL(jz_init_clocks); + diff --git a/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock.h b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock.h new file mode 100644 index 0000000000..88ce07d16d --- /dev/null +++ b/target/linux/xburst/files-2.6.32/arch/mips/jz4740/clock.h @@ -0,0 +1,62 @@ +/* + * 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 + * 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. + * + * 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 02139, USA. + * + */ + +#ifndef __JZ4740_CLOCK_H__ +#define __JZ4740_CLOCK_H__ + +struct clk_ops { + 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 (*is_enabled)(struct clk* clk); + + int (*set_parent)(struct clk* clk, struct clk *parent); + +}; + +struct clk { + const char *name; + struct clk* parent; + + uint32_t gate_bit; + + const struct clk_ops *ops; + + struct list_head list; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_entry; + struct dentry *debugfs_parent_entry; +#endif + +}; + +#define JZ4740_CLK_NOT_GATED ((uint32_t)-1) + +int clk_is_enabled(struct clk *clk); + +#ifdef CONFIG_DEBUG_FS +void jz4740_clock_debugfs_init(void); +void jz4740_clock_debugfs_add_clk(struct clk *clk); +void jz4740_clock_debugfs_update_parent(struct clk *clk); +#else +static inline void jz4740_clock_debugfs_init(void) {}; +static inline void jz4740_clock_debugfs_add_clk(struct clk *clk) {}; +static inline void jz4740_clock_debugfs_update_parent(struct clk *clk) {}; +#endif + +#endif |