diff options
author | Michael Turquette <mturquette@baylibre.com> | 2015-01-22 17:13:16 +0100 |
---|---|---|
committer | Michael Turquette <mturquette@baylibre.com> | 2016-11-17 19:20:05 -0800 |
commit | 22a93288e46c75bb9a1dc5730d9be7b0bda93d94 (patch) | |
tree | 617e57343982274b2f8f355fac394bd1bfddab10 | |
parent | e902ba1f6ae52feec6a457ea786dd6ce07bdf859 (diff) | |
download | linux-ccr-narmstrong-squash.tar.gz |
clk: Add module for unit testsccr-narmstrong-squash
This module registers a simple two-clock hierarchy and calls
clk_set_rate on it a few times to test the new coordinated clk rates
feature.
Signed-off-by: Michael Turquette <mturquette@baylibre.com>
-rw-r--r-- | drivers/clk/Kconfig | 1 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/test/Kconfig | 16 | ||||
-rw-r--r-- | drivers/clk/test/Makefile | 5 | ||||
-rw-r--r-- | drivers/clk/test/clk-test-cr.c | 400 | ||||
-rw-r--r-- | drivers/clk/test/clk-test.c | 68 | ||||
-rw-r--r-- | drivers/clk/test/clk-test.h | 24 |
7 files changed, 515 insertions, 0 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index e2d9bd760c84a..1b5f6374352aa 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -210,6 +210,7 @@ config COMMON_CLK_OXNAS source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" +source "drivers/clk/test/Kconfig" source "drivers/clk/meson/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/qcom/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3b6f9cf3464aa..d01b8e9f0c312 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -85,6 +85,7 @@ obj-$(CONFIG_ARCH_STI) += st/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ +obj-$(CONFIG_COMMON_CLK_TEST) += test/ obj-y += ti/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ diff --git a/drivers/clk/test/Kconfig b/drivers/clk/test/Kconfig new file mode 100644 index 0000000000000..fd7be7dff331b --- /dev/null +++ b/drivers/clk/test/Kconfig @@ -0,0 +1,16 @@ +config COMMON_CLK_TEST + tristate "Unit tests for the Common Clock Framework" + ---help--- + This option allows for selection of various unit tests. Selecting it + also builds the clk_test_provider driver which runs the optional + tests found under this config option + +config COMMON_CLK_TEST_CR + tristate "Unit tests for coordinated clock rates" + depends on COMMON_CLK_TEST + ---help--- + clk_test_provider registers clocks with coordinated rates using both + static rate tables and dynamic rates. + Test description: rate change operations are done at .init. State + changes may be observed if debug prints are enabled. The final state + of the coordinated clocks may also be inspected via debugfs diff --git a/drivers/clk/test/Makefile b/drivers/clk/test/Makefile new file mode 100644 index 0000000000000..07e6556952977 --- /dev/null +++ b/drivers/clk/test/Makefile @@ -0,0 +1,5 @@ +# clock provider driver that runs all unit tests +obj-$(CONFIG_COMMON_CLK_TEST) += clk-test.o + +# unit tests +obj-$(CONFIG_COMMON_CLK_TEST_CR) += clk-test-cr.o diff --git a/drivers/clk/test/clk-test-cr.c b/drivers/clk/test/clk-test-cr.c new file mode 100644 index 0000000000000..91bb7834ca511 --- /dev/null +++ b/drivers/clk/test/clk-test-cr.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2015 BayLibre, Inc. + * Michael Turquette <mturquette@baylibre.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Unit tests for coordinated rates + */ + +#include <linux/clk-provider.h> +//#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/module.h> +#if 0 +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/init.h> +#include <linux/printk.h> +#endif + +#define NR_CLK 4 + +/* + * This unit test implements two sets of clocks and ops to test the coordinated + * rate infrastructure: + * + * 1) statically initialized rate tables + * 2) dynamic clock rates without tables + * + * The clock tree hierarchy used is a real example borrowed from clk-ls1x.c, + * but the rates are contrived for this example: + * _____ + * _______________________| | + * OSC ___/ | MUX |___ CPU CLK + * \___ PLL ___ CPU DIV ___| | + * |_____| + * + * FIXME move all text below somewhere else... + * Besides having fine-grained control over the rate at each node in this + * graph, using coordinated rates allows the clock provider driver to precisely + * control the order operations. For instance, a mux clock might need to + * temporarily switch parents during a transition. The beginning and ending + * parent are the same, but using a .set_cr_state callback give full control to + * driver over the mux during transition. + */ + +/* + * test_osc is used in both the static tables and dynamic rates examples + * note that test_osc is not a member of the of the cr_domain in the static + * tables example + */ +static struct clk_fixed_rate test_osc = { + .fixed_rate = 24000000, + .hw.init = &(struct clk_init_data){ + .name = "test_osc", + .num_parents = 0, + .ops = &clk_fixed_rate_ops, + }, +}; + +/* + * ===================================================================== + * static rate tables example + * ===================================================================== + */ + +struct test_clk_static { + struct clk_hw hw; + struct cr_domain *domain; + /* + * Normally the info from the three struct members below would come + * from reading the state from hardware. Instead we used cache values + * because these clocks are fake + */ + unsigned long pll_rate; + int div; + u8 parent_idx; +}; + +/* private data used in struct cr_state */ +struct test_clk_priv_data { + unsigned long pll_rate; + int post_divider_div; + u8 cpu_mux_parent_idx; +}; + +/* clk_ops */ + +/* + * all of the clk_ops below rely on the cached values declared above in struct + * test_clk_static. Normally these values would come from reading the hardware + * state + */ +#define to_test_clk_static(_hw) container_of(_hw, struct test_clk_static, hw) + +static unsigned long test_clk_pll_static_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct test_clk_static *test_clk_static = to_test_clk_static(hw); + + return test_clk_static->pll_rate; +} + +static unsigned long test_clk_div_static_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct test_clk_static *test_clk_static = to_test_clk_static(hw); + + return parent_rate / test_clk_static->div; +} + +static u8 test_clk_static_get_parent(struct clk_hw *hw) +{ + struct test_clk_static *test = to_test_clk_static(hw); + + return test->parent_idx; +} + +static struct cr_state *test_clk_static_get_cr_state(struct clk_hw *hw, + unsigned long rate) +{ + struct cr_state *state; + struct test_clk_static *test = to_test_clk_static(hw); + + state = clk_get_cr_state_from_domain(hw, test->domain, rate); + if (IS_ERR(state)) + pr_err("%s: failed to get cr_state for clk %s with code %ld\n", + __func__, clk_hw_get_name(hw), PTR_ERR(state)); + + return state; +} + +static int test_clk_static_set_cr_state(const struct cr_state *state) +{ + int i; + struct cr_clk *cr_clk; + struct test_clk_static *test_clk; + struct test_clk_priv_data *priv = state->priv; + + pr_debug("%s: setting cr_state:\n", __func__); + for (i = 0; i < state->nr_clk; i++) { + cr_clk = state->clks[i]; + pr_debug("%s: clk %s, rate %lu, parent %s\n", __func__, + clk_hw_get_name(cr_clk->hw), cr_clk->rate, + clk_hw_get_name(cr_clk->parent_hw)); + } + + /* + * XXX note to clock provider driver implementers: + * + * machine-specific register writes would go here for an implementation + * on real hardware, perhaps making use of the cr_state->priv data. + * After setting the hardware, the clock framework will read back this + * info in the usual .recalc_rate and .get_parent callbacks. + * + * For this unit test we store cached values for pll rate, post-divider + * divisor, and mux parent in memory so that .recalc_rate and + * .get_parent work correctly. Those callbacks simply return the cached + * values. + */ + test_clk = to_test_clk_static(state->clks[0]->hw); + test_clk->pll_rate = priv->pll_rate; + test_clk = to_test_clk_static(state->clks[1]->hw); + test_clk->div = priv->post_divider_div; + test_clk = to_test_clk_static(state->clks[2]->hw); + test_clk->parent_idx = priv->cpu_mux_parent_idx; + + return 0; +} + +/* separate clk_ops are not necessary here, but aid readability */ + +/* pll requires .recalc_rate */ +static const struct clk_ops test_clk_pll_static_ops = { + .recalc_rate = test_clk_pll_static_recalc_rate, + .get_cr_state = test_clk_static_get_cr_state, + .set_cr_state = test_clk_static_set_cr_state, +}; + +/* post divider requires .recalc_rate */ +static const struct clk_ops test_clk_div_static_ops = { + .recalc_rate = test_clk_div_static_recalc_rate, + .get_cr_state = test_clk_static_get_cr_state, + .set_cr_state = test_clk_static_set_cr_state, +}; + +/* cpu mux requires .get_parent */ +static const struct clk_ops test_clk_mux_static_ops = { + .get_parent = test_clk_static_get_parent, + .get_cr_state = test_clk_static_get_cr_state, + .set_cr_state = test_clk_static_set_cr_state, +}; + +/* forward declaration to keep things tidy */ +static struct cr_domain test_static_cr_domain; + +static struct test_clk_static test_static_pll = { + .pll_rate = 1000000000, + .domain = &test_static_cr_domain, + .hw.init = &(struct clk_init_data){ + .name = "test_static_pll", + .ops = &test_clk_pll_static_ops, + .parent_names = (const char *[]){ "test_osc" }, + .num_parents = 1, + }, +}; + +static struct test_clk_static test_static_div = { + .div = 1, + .domain = &test_static_cr_domain, + .hw.init = &(struct clk_init_data){ + .name = "test_static_div", + .ops = &test_clk_div_static_ops, + .parent_names = (const char *[]){ "test_static_pll" }, + .num_parents = 1, + }, +}; + +static struct test_clk_static test_static_mux = { + .parent_idx = 0, + .domain = &test_static_cr_domain, + .hw.init = &(struct clk_init_data){ + .name = "test_static_mux", + .ops = &test_clk_mux_static_ops, + .parent_names = (const char *[]){ "test_osc", + "test_static_div" }, + .num_parents = 2, + }, +}; + +/* machine-specific private data used in struct cr_state */ +struct test_clk_priv_data bypass = { 1000000000, 2, 0 }; +struct test_clk_priv_data middle = { 1000000000, 2, 1 }; +struct test_clk_priv_data high = { 1000000000, 1, 1 }; + +/* low frequency, bypassing the pll */ +static struct cr_state state_bypass = { + .nr_clk = 3, + .priv = &bypass, + .needs_free = false, + .clks = { + &(struct cr_clk){ + .hw = &test_static_pll.hw, + .parent_hw = &test_osc.hw, + .rate = 1000000000, + .is_root = true, + }, + &(struct cr_clk){ + .hw = &test_static_div.hw, + .parent_hw = &test_static_pll.hw, + .rate = 500000000, + .is_root = false, + }, + &(struct cr_clk){ + .hw = &test_static_mux.hw, + .parent_hw = &test_osc.hw, + .rate = 24000000, + .is_root = true, + }, + }, +}; + +/* middle frequency, dividing pll by 2 */ +static struct cr_state state_middle = { + .nr_clk = 3, + .priv = &middle, + .needs_free = false, + .clks = { + &(struct cr_clk){ + .hw = &test_static_pll.hw, + .parent_hw = &test_osc.hw, + .rate = 1000000000, + .is_root = true, + }, + &(struct cr_clk){ + .hw = &test_static_div.hw, + .parent_hw = &test_static_pll.hw, + .rate = 500000000, + .is_root = false, + }, + &(struct cr_clk){ + .hw = &test_static_mux.hw, + .parent_hw = &test_static_div.hw, + .rate = 500000000, + .is_root = false, + }, + }, +}; + +/* high frequency at full pll rate */ +static struct cr_state state_high = { + .nr_clk = 3, + .priv = &high, + .needs_free = false, + .clks = { + &(struct cr_clk){ + .hw = &test_static_pll.hw, + .parent_hw = &test_osc.hw, + .rate = 1000000000, + .is_root = true, + }, + &(struct cr_clk){ + .hw = &test_static_div.hw, + .parent_hw = &test_static_pll.hw, + .rate = 1000000000, + .is_root = false, + }, + &(struct cr_clk){ + .hw = &test_static_mux.hw, + .parent_hw = &test_static_div.hw, + .rate = 1000000000, + .is_root = false, + }, + }, +}; + +/* + * XXX note to clock provider drivers implementers: + * + * cr_domain is an optional helper data structure. It provides a useful + * starting point for tables of discretized rates. It is possible to invent + * your own data structure for tables without using struct cr_domain. The clk + * core does not use cr_domain at all. However, struct cr_domain does pair + * nicely with clk_get_cr_state_from_domain, an optional helper function for + * finding the first matching cr_state for a given (clk,rate) pair. + */ +static struct cr_domain test_static_cr_domain = { + .nr_state = 3, + .priv = NULL, + .states = { &state_bypass, &state_middle, &state_high, }, +}; + +/* + * ===================================================================== + * dynamic rate tables example + * ===================================================================== + */ + +/* + * ===================================================================== + * module boilerplate + * ===================================================================== + */ + +struct clk_hw *clk_test_cr_hw[] = { + &test_osc.hw, + &test_static_pll.hw, + &test_static_div.hw, + &test_static_mux.hw, + //&test_dynamic_pll.hw, + //&test_dynamic_div.hw, + //&test_dynamic_cpu_mux.hw, +}; + +int clk_test_cr_probe(void) +{ + int i, ret; + struct clk *cpu_mux; + + pr_err("%s: I'm here!\n", __func__); + + /* + * register all clks + */ + for (i = 0; i < NR_CLK; i++) { + ret = clk_hw_register(NULL, clk_test_cr_hw[i]); + clk_hw_register_clkdev(clk_test_cr_hw[i], clk_test_cr_hw[i]->init->name, NULL); + pr_debug("%s: clk_test_cr_hw[i]->init->name %s\n", __func__, clk_test_cr_hw[i]->init->name); + if (ret) + pr_err("%s: unable to register test_clk_cr hw\n", __func__); + } + + /* run the tests for static table clocks */ + cpu_mux = clk_get(NULL, "test_static_mux"); + if (IS_ERR(cpu_mux)) { + pr_err("%s: could not get cpu_mux clk %ld\n", __func__, PTR_ERR(cpu_mux)); + return 0; + } + pr_debug("%s: cpu_mux rate is %lu\n", __func__, clk_get_rate(cpu_mux)); + + clk_set_rate(cpu_mux, 1000000000); + pr_debug("%s: cpu_mux rate is %lu\n", __func__, clk_get_rate(cpu_mux)); + + clk_set_rate(cpu_mux, 500000000); + pr_debug("%s: cpu_mux rate is %lu\n", __func__, clk_get_rate(cpu_mux)); + + clk_set_rate(cpu_mux, 24000000); + pr_debug("%s: cpu_mux rate is %lu\n", __func__, clk_get_rate(cpu_mux)); + + clk_set_rate(cpu_mux, 1000000000); + pr_debug("%s: cpu_mux rate is %lu\n", __func__, clk_get_rate(cpu_mux)); + + return 0; +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Michael Turquette <mturquette@baylibre.com>"); diff --git a/drivers/clk/test/clk-test.c b/drivers/clk/test/clk-test.c new file mode 100644 index 0000000000000..cf62f9bd6ee51 --- /dev/null +++ b/drivers/clk/test/clk-test.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 BayLibre, Inc <mturquette@baylibre.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Clock provider for CCF unit tests + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include "clk-test.h" + +//static int clk_test_probe(struct platform_device *pdev) +static int clk_test_probe(void) +{ + pr_err("%s: here\n", __func__); + clk_test_cr_probe(NULL); + return 0; +} + +#if 0 +static int clk_test_remove(struct platform_device *pdev) +{ + clk_test_cr_remove(pdev); + return 0; +} + +static void clk_test_shutdown(struct platform_device *pdev) +{ + clk_test_cr_shutdown(pdev); +} + +static int clk_test_suspend(struct platform_device *pdev, pm_message_t state) +{ + clk_test_cr_suspend(pdev, state); + return 0; +} + +static int clk_test_resume(struct platform_device *pdev) +{ + clk_test_cr_resume(pdev); + return 0; +} +#endif + +#if 0 +static struct platform_driver clk_test_driver = { + .driver = { + .name = "clk_test", + //.of_match_table = clk_test_ids, + }, + .probe = clk_test_probe, +#if 0 + .remove = clk_test_remove, + .shutdown = clk_test_shutdown, + .suspend = clk_test_suspend, + .resume = clk_test_resume, +#endif +}; +#endif +//module_platform_driver(clk_test_driver); +module_init(clk_test_probe); + +MODULE_AUTHOR("Michael Turquette <mturquette@baylibre.com>"); +MODULE_DESCRIPTION("Common Clock Framework Unit Tests"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/test/clk-test.h b/drivers/clk/test/clk-test.h new file mode 100644 index 0000000000000..627e3e637bcf1 --- /dev/null +++ b/drivers/clk/test/clk-test.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 BayLibre, Inc <mturquette@baylibre.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * CCF unit tests + */ + +#ifdef CONFIG_COMMON_CLK_TEST_CR +int clk_test_cr_probe(struct platform_device *pdev); +int clk_test_cr_remove(struct platform_device *pdev); +void clk_test_cr_shutdown(struct platform_device *pdev); +int clk_test_cr_suspend(struct platform_device *pdev, pm_message_t state); +int clk_test_cr_resume(struct platform_device *pdev); +#else +static inline int clk_test_cr_probe(struct platform_device *pdev) { return 0; } +static inline int clk_test_cr_remove(struct platform_device *pdev) { return 0;} +static inline void clk_test_cr_shutdown(struct platform_device *pdev) { return; } +static inline int clk_test_cr_suspend(struct platform_device *pdev, + pm_message_t state) { return 0; } +static inline int clk_test_cr_resume(struct platform_device *pdev) { return 0; } +#endif |