diff options
author | Mike Turquette <mturquette@linaro.org> | 2014-09-27 12:52:33 -0700 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-09-27 12:52:33 -0700 |
commit | 4dc7ed32f398fa76b9e1d243a852420b1dad0150 (patch) | |
tree | a040f8c006ea7a7a3c962f135c8efd8b72cbc4b4 /drivers/clk/clk.c | |
parent | 5ad67d3e5e0a5059945a7726a407763a23f80d9e (diff) | |
parent | 9c8176bfb67f98ed9a521b624dcb6ab7fa254aa7 (diff) | |
download | op-kernel-dev-4dc7ed32f398fa76b9e1d243a852420b1dad0150.zip op-kernel-dev-4dc7ed32f398fa76b9e1d243a852420b1dad0150.tar.gz |
Merge tag 'sunxi-clocks-for-3.18' of git://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux into clk-next
Allwinner Clocks Additions for 3.18
The most important part of this serie is the addition of the phase API to
handle the MMC clocks in the Allwinner SoCs.
Apart from that, the A23 gained a new mbus driver, and there's a fix for a
incorrect divider table on the APB0 clock.
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r-- | drivers/clk/clk.c | 95 |
1 files changed, 91 insertions, 4 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 52d5827..4896ae9 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -119,11 +119,11 @@ static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) if (!c) return; - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu\n", + seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", level * 3 + 1, "", 30 - level * 3, c->name, c->enable_count, c->prepare_count, clk_get_rate(c), - clk_get_accuracy(c)); + clk_get_accuracy(c), clk_get_phase(c)); } static void clk_summary_show_subtree(struct seq_file *s, struct clk *c, @@ -145,8 +145,8 @@ static int clk_summary_show(struct seq_file *s, void *data) struct clk *c; struct hlist_head **lists = (struct hlist_head **)s->private; - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy\n"); - seq_puts(s, "--------------------------------------------------------------------------------\n"); + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------\n"); clk_prepare_lock(); @@ -182,6 +182,7 @@ static void clk_dump_one(struct seq_file *s, struct clk *c, int level) seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); seq_printf(s, "\"rate\": %lu", clk_get_rate(c)); seq_printf(s, "\"accuracy\": %lu", clk_get_accuracy(c)); + seq_printf(s, "\"phase\": %d", clk_get_phase(c)); } static void clk_dump_subtree(struct seq_file *s, struct clk *c, int level) @@ -266,6 +267,11 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry) if (!d) goto err_out; + d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry, + (u32 *)&clk->phase); + if (!d) + goto err_out; + d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry, (u32 *)&clk->flags); if (!d) @@ -1726,6 +1732,77 @@ out: EXPORT_SYMBOL_GPL(clk_set_parent); /** + * clk_set_phase - adjust the phase shift of a clock signal + * @clk: clock signal source + * @degrees: number of degrees the signal is shifted + * + * Shifts the phase of a clock signal by the specified + * degrees. Returns 0 on success, -EERROR otherwise. + * + * This function makes no distinction about the input or reference + * signal that we adjust the clock signal phase against. For example + * phase locked-loop clock signal generators we may shift phase with + * respect to feedback clock signal input, but for other cases the + * clock phase may be shifted with respect to some other, unspecified + * signal. + * + * Additionally the concept of phase shift does not propagate through + * the clock tree hierarchy, which sets it apart from clock rates and + * clock accuracy. A parent clock phase attribute does not have an + * impact on the phase attribute of a child clock. + */ +int clk_set_phase(struct clk *clk, int degrees) +{ + int ret = 0; + + if (!clk) + goto out; + + /* sanity check degrees */ + degrees %= 360; + if (degrees < 0) + degrees += 360; + + clk_prepare_lock(); + + if (!clk->ops->set_phase) + goto out_unlock; + + ret = clk->ops->set_phase(clk->hw, degrees); + + if (!ret) + clk->phase = degrees; + +out_unlock: + clk_prepare_unlock(); + +out: + return ret; +} + +/** + * clk_get_phase - return the phase shift of a clock signal + * @clk: clock signal source + * + * Returns the phase shift of a clock node in degrees, otherwise returns + * -EERROR. + */ +int clk_get_phase(struct clk *clk) +{ + int ret = 0; + + if (!clk) + goto out; + + clk_prepare_lock(); + ret = clk->phase; + clk_prepare_unlock(); + +out: + return ret; +} + +/** * __clk_init - initialize the data structures in a struct clk * @dev: device initializing this clk, placeholder for now * @clk: clk being initialized @@ -1844,6 +1921,16 @@ int __clk_init(struct device *dev, struct clk *clk) clk->accuracy = 0; /* + * Set clk's phase. + * Since a phase is by definition relative to its parent, just + * query the current clock phase, or just assume it's in phase. + */ + if (clk->ops->get_phase) + clk->phase = clk->ops->get_phase(clk->hw); + else + clk->phase = 0; + + /* * Set clk's rate. The preferred method is to use .recalc_rate. For * simple clocks and lazy developers the default fallback is to use the * parent's rate. If a clock doesn't have a parent (or is orphaned) |