From 5aefa34fada9d17a00635516688de34702451708 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 1 Nov 2010 15:30:31 -0400 Subject: sh: clkfwk: Fix up rate rounding error handling. According to the linux/clk.h definition we should be handing back an errno value or a valid rate. This fixes up the case where 0 can be returned for invalid frequencies or cases where rounding has no selectable candidate. Reported-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/sh/clk/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/sh') diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index fd0d1b98901c..861144360d89 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -90,8 +90,8 @@ struct clk_rate_round_data { static long clk_rate_round_helper(struct clk_rate_round_data *rounder) { unsigned long rate_error, rate_error_prev = ~0UL; - unsigned long rate_best_fit = rounder->rate; unsigned long highest, lowest, freq; + long rate_best_fit = -ENOENT; int i; highest = 0; @@ -146,7 +146,7 @@ long clk_rate_table_round(struct clk *clk, }; if (clk->nr_freqs < 1) - return 0; + return -ENOSYS; return clk_rate_round_helper(&table_round); } -- cgit v1.2.3 From 20f95e0b22ea45c1798261064baab57efeb3b3bc Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 1 Nov 2010 16:10:48 -0400 Subject: sh: intc: Update for single IRQ reservation helper. Signed-off-by: Paul Mundt --- drivers/sh/intc/core.c | 2 +- drivers/sh/intc/dynamic.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/sh') diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 873a99ff8f64..e5e9e6735f7d 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -79,7 +79,7 @@ static void __init intc_register_irq(struct intc_desc *desc, * Register the IRQ position with the global IRQ map, then insert * it in to the radix tree. */ - irq_reserve_irqs(irq, 1); + irq_reserve_irq(irq); raw_spin_lock_irqsave(&intc_big_lock, flags); radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq)); diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c index 4187cce20ffd..a3677c9dfe36 100644 --- a/drivers/sh/intc/dynamic.c +++ b/drivers/sh/intc/dynamic.c @@ -60,5 +60,5 @@ void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) int i; for (i = 0; i < nr_vecs; i++) - irq_reserve_irqs(evt2irq(vectors[i].vect), 1); + irq_reserve_irq(evt2irq(vectors[i].vect)); } -- cgit v1.2.3 From 6af26c6c99f01e810f9944543df810e320284aa3 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 2 Nov 2010 11:27:24 +0000 Subject: sh: add clk_round_parent() to optimize parent clock rate Sometimes it is possible and reasonable to adjust the parent clock rate to improve precision of the child clock, e.g., if the child clock has no siblings. clk_round_parent() is a new addition to the SH clock-framework API, that implements such an optimization for child clocks with divisors, taking all integer values in a range. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Paul Mundt --- drivers/sh/clk/core.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) (limited to 'drivers/sh') diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index 861144360d89..b3840597ad6e 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -541,6 +541,81 @@ long clk_round_rate(struct clk *clk, unsigned long rate) } EXPORT_SYMBOL_GPL(clk_round_rate); +long clk_round_parent(struct clk *clk, unsigned long target, + unsigned long *best_freq, unsigned long *parent_freq, + unsigned int div_min, unsigned int div_max) +{ + struct cpufreq_frequency_table *freq, *best = NULL; + unsigned long error = ULONG_MAX, freq_high, freq_low, div; + struct clk *parent = clk_get_parent(clk); + + if (!parent) { + *parent_freq = 0; + *best_freq = clk_round_rate(clk, target); + return abs(target - *best_freq); + } + + for (freq = parent->freq_table; freq->frequency != CPUFREQ_TABLE_END; + freq++) { + if (freq->frequency == CPUFREQ_ENTRY_INVALID) + continue; + + if (unlikely(freq->frequency / target <= div_min - 1)) { + unsigned long freq_max = (freq->frequency + div_min / 2) / div_min; + if (error > target - freq_max) { + error = target - freq_max; + best = freq; + if (best_freq) + *best_freq = freq_max; + } + pr_debug("too low freq %lu, error %lu\n", freq->frequency, target - freq_max); + if (!error) + break; + continue; + } + + if (unlikely(freq->frequency / target >= div_max)) { + unsigned long freq_min = (freq->frequency + div_max / 2) / div_max; + if (error > freq_min - target) { + error = freq_min - target; + best = freq; + if (best_freq) + *best_freq = freq_min; + } + pr_debug("too high freq %lu, error %lu\n", freq->frequency, freq_min - target); + if (!error) + break; + continue; + } + + + div = freq->frequency / target; + freq_high = freq->frequency / div; + freq_low = freq->frequency / (div + 1); + if (freq_high - target < error) { + error = freq_high - target; + best = freq; + if (best_freq) + *best_freq = freq_high; + } + if (target - freq_low < error) { + error = target - freq_low; + best = freq; + if (best_freq) + *best_freq = freq_low; + } + pr_debug("%u / %lu = %lu, / %lu = %lu, best %lu, parent %u\n", + freq->frequency, div, freq_high, div + 1, freq_low, + *best_freq, best->frequency); + if (!error) + break; + } + if (parent_freq) + *parent_freq = best->frequency; + return error; +} +EXPORT_SYMBOL_GPL(clk_round_parent); + #ifdef CONFIG_PM static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state) { -- cgit v1.2.3 From a766b29790b2b6582345624a6e9e686d8015efe1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 8 Nov 2010 09:40:23 +0900 Subject: sh: clkfwk: Fix up checkpatch warnings. The clk_round_parent() change introduced various checkpatch warnings, tidy them up. Signed-off-by: Paul Mundt --- drivers/sh/clk/core.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'drivers/sh') diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c index b3840597ad6e..09615b51d591 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -561,57 +561,74 @@ long clk_round_parent(struct clk *clk, unsigned long target, continue; if (unlikely(freq->frequency / target <= div_min - 1)) { - unsigned long freq_max = (freq->frequency + div_min / 2) / div_min; + unsigned long freq_max; + + freq_max = (freq->frequency + div_min / 2) / div_min; if (error > target - freq_max) { error = target - freq_max; best = freq; if (best_freq) *best_freq = freq_max; } - pr_debug("too low freq %lu, error %lu\n", freq->frequency, target - freq_max); + + pr_debug("too low freq %lu, error %lu\n", freq->frequency, + target - freq_max); + if (!error) break; + continue; } if (unlikely(freq->frequency / target >= div_max)) { - unsigned long freq_min = (freq->frequency + div_max / 2) / div_max; + unsigned long freq_min; + + freq_min = (freq->frequency + div_max / 2) / div_max; if (error > freq_min - target) { error = freq_min - target; best = freq; if (best_freq) *best_freq = freq_min; } - pr_debug("too high freq %lu, error %lu\n", freq->frequency, freq_min - target); + + pr_debug("too high freq %lu, error %lu\n", freq->frequency, + freq_min - target); + if (!error) break; + continue; } - div = freq->frequency / target; freq_high = freq->frequency / div; freq_low = freq->frequency / (div + 1); + if (freq_high - target < error) { error = freq_high - target; best = freq; if (best_freq) *best_freq = freq_high; } + if (target - freq_low < error) { error = target - freq_low; best = freq; if (best_freq) *best_freq = freq_low; } + pr_debug("%u / %lu = %lu, / %lu = %lu, best %lu, parent %u\n", freq->frequency, div, freq_high, div + 1, freq_low, *best_freq, best->frequency); + if (!error) break; } + if (parent_freq) *parent_freq = best->frequency; + return error; } EXPORT_SYMBOL_GPL(clk_round_parent); -- cgit v1.2.3