summaryrefslogtreecommitdiff
path: root/drivers/clk/at91
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-04-21 19:24:09 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-21 19:24:09 +0300
commite98bf5cedf25eb32f8f224e8dff9845f0856d18f (patch)
treef8a33eb26cb9d47bfbb36e50de7ad85081cc349e /drivers/clk/at91
parent8f443e2372ba23d51ee365974f54507acd6f69d1 (diff)
parent03bc10ab5b0f9b8f81bffbe6e40c944f9d3dbcc5 (diff)
downloadlinux-e98bf5cedf25eb32f8f224e8dff9845f0856d18f.tar.xz
Merge tag 'clk-for-linus-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clock framework updates from Michael Turquette: "The changes to the common clock framework for 4.0 are mostly new clock drivers and updates to existing ones for feature enhancements and bug fixes. There is more churn than usual in the framework core due to the change to introduce per-user unique struct clk pointers in 4.0. This caused several regressions to surface, some of which were sent as fixes to 4.0. New generic clock drivers were added for GPIO- and PWM-based clock controllers. Additionally the common clk-divider code recieved several fixes to the way it rounds rates" * tag 'clk-for-linus-4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (91 commits) clk: check ->determine/round_rate() return value in clk_calc_new_rates clk: at91: usb: propagate rate modification to the parent clk clk: samsung: exynos4: Disable ARMCLK down feature on Exynos4210 SoC clk: don't use __initconst for non-const arrays clk: at91: change to using endian agnositc IO clk: clk-gpio-gate: Fix active low clk: Add PWM clock driver clk: Add clock driver for mb86s7x clk: pxa: pxa3xx: add missing os timer clock clk: tegra: Use the proper parent for plld_dsi clk: tegra: Use generic tegra_osc_clk_init() on Tegra114 clk: tegra: Model oscillator as clock clk: tegra: Add peripheral registers for bank Y clk: tegra: Register the proper number of resets clk: tegra: Remove needless initializations clk: tegra: Use consistent indentation clk: tegra: Various whitespace cleanups clk: tegra: Enable HDA to HDMI clocks on Tegra124 clk: tegra: Fix a bunch of sparse warnings clk: tegra: Fix typo tabel -> table ...
Diffstat (limited to 'drivers/clk/at91')
-rw-r--r--drivers/clk/at91/clk-usb.c64
1 files changed, 49 insertions, 15 deletions
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index a23ac0c724f0..0b7c3e8840ba 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -56,22 +56,55 @@ static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
}
-static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static long at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long min_rate,
+ unsigned long max_rate,
+ unsigned long *best_parent_rate,
+ struct clk_hw **best_parent_hw)
{
- unsigned long div;
+ struct clk *parent = NULL;
+ long best_rate = -EINVAL;
+ unsigned long tmp_rate;
+ int best_diff = -1;
+ int tmp_diff;
+ int i;
- if (!rate)
- return -EINVAL;
+ for (i = 0; i < __clk_get_num_parents(hw->clk); i++) {
+ int div;
- if (rate >= *parent_rate)
- return *parent_rate;
+ parent = clk_get_parent_by_index(hw->clk, i);
+ if (!parent)
+ continue;
+
+ for (div = 1; div < SAM9X5_USB_MAX_DIV + 2; div++) {
+ unsigned long tmp_parent_rate;
+
+ tmp_parent_rate = rate * div;
+ tmp_parent_rate = __clk_round_rate(parent,
+ tmp_parent_rate);
+ tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div);
+ if (tmp_rate < rate)
+ tmp_diff = rate - tmp_rate;
+ else
+ tmp_diff = tmp_rate - rate;
+
+ if (best_diff < 0 || best_diff > tmp_diff) {
+ best_rate = tmp_rate;
+ best_diff = tmp_diff;
+ *best_parent_rate = tmp_parent_rate;
+ *best_parent_hw = __clk_get_hw(parent);
+ }
+
+ if (!best_diff || tmp_rate < rate)
+ break;
+ }
- div = DIV_ROUND_CLOSEST(*parent_rate, rate);
- if (div > SAM9X5_USB_MAX_DIV + 1)
- div = SAM9X5_USB_MAX_DIV + 1;
+ if (!best_diff)
+ break;
+ }
- return DIV_ROUND_CLOSEST(*parent_rate, div);
+ return best_rate;
}
static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index)
@@ -121,7 +154,7 @@ static int at91sam9x5_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops at91sam9x5_usb_ops = {
.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
- .round_rate = at91sam9x5_clk_usb_round_rate,
+ .determine_rate = at91sam9x5_clk_usb_determine_rate,
.get_parent = at91sam9x5_clk_usb_get_parent,
.set_parent = at91sam9x5_clk_usb_set_parent,
.set_rate = at91sam9x5_clk_usb_set_rate,
@@ -159,7 +192,7 @@ static const struct clk_ops at91sam9n12_usb_ops = {
.disable = at91sam9n12_clk_usb_disable,
.is_enabled = at91sam9n12_clk_usb_is_enabled,
.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
- .round_rate = at91sam9x5_clk_usb_round_rate,
+ .determine_rate = at91sam9x5_clk_usb_determine_rate,
.set_rate = at91sam9x5_clk_usb_set_rate,
};
@@ -179,7 +212,8 @@ at91sam9x5_clk_register_usb(struct at91_pmc *pmc, const char *name,
init.ops = &at91sam9x5_usb_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
- init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+ CLK_SET_RATE_PARENT;
usb->hw.init = &init;
usb->pmc = pmc;
@@ -207,7 +241,7 @@ at91sam9n12_clk_register_usb(struct at91_pmc *pmc, const char *name,
init.ops = &at91sam9n12_usb_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
- init.flags = CLK_SET_RATE_GATE;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT;
usb->hw.init = &init;
usb->pmc = pmc;