diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2015-03-28 21:45:33 -0400 |
---|---|---|
committer | Michael Turquette <mturquette@linaro.org> | 2015-04-13 00:08:51 -0400 |
commit | 4591243102faa8de92da320edea47219901461e9 (patch) | |
tree | 896a83de022e746750b31953b2fef4b78fffa537 /drivers/clk | |
parent | 3a9e9cb65be84d6c64fbe9c69a73c15d59f29454 (diff) |
clk: at91: usb: propagate rate modification to the parent clk
The at91sam9n12 and at91sam9x5 usb clocks do not propagate rate
modification requests to their parents.
This causes a bug when the PLLB is left uninitialized by the bootloader
(PLL multiplier set to 0, or in other words, PLL rate = 0 Hz).
Implement the determinate_rate method and propagate the change rate
request to the parent clk.
Cc: <stable@vger.kernel.org> # v3.14+
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Reported-by: Bo Shen <voice.shen@atmel.com>
Tested-by: Bo Shen <voice.shen@atmel.com>
Signed-off-by: Michael Turquette <mturquette@linaro.org>
Diffstat (limited to 'drivers/clk')
-rw-r--r-- | drivers/clk/at91/clk-usb.c | 64 |
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, | |||
56 | return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); | 56 | return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1)); |
57 | } | 57 | } |
58 | 58 | ||
59 | static long at91sam9x5_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate, | 59 | static long at91sam9x5_clk_usb_determine_rate(struct clk_hw *hw, |
60 | unsigned long *parent_rate) | 60 | unsigned long rate, |
61 | unsigned long min_rate, | ||
62 | unsigned long max_rate, | ||
63 | unsigned long *best_parent_rate, | ||
64 | struct clk_hw **best_parent_hw) | ||
61 | { | 65 | { |
62 | unsigned long div; | 66 | struct clk *parent = NULL; |
67 | long best_rate = -EINVAL; | ||
68 | unsigned long tmp_rate; | ||
69 | int best_diff = -1; | ||
70 | int tmp_diff; | ||
71 | int i; | ||
63 | 72 | ||
64 | if (!rate) | 73 | for (i = 0; i < __clk_get_num_parents(hw->clk); i++) { |
65 | return -EINVAL; | 74 | int div; |
66 | 75 | ||
67 | if (rate >= *parent_rate) | 76 | parent = clk_get_parent_by_index(hw->clk, i); |
68 | return *parent_rate; | 77 | if (!parent) |
78 | continue; | ||
79 | |||
80 | for (div = 1; div < SAM9X5_USB_MAX_DIV + 2; div++) { | ||
81 | unsigned long tmp_parent_rate; | ||
82 | |||
83 | tmp_parent_rate = rate * div; | ||
84 | tmp_parent_rate = __clk_round_rate(parent, | ||
85 | tmp_parent_rate); | ||
86 | tmp_rate = DIV_ROUND_CLOSEST(tmp_parent_rate, div); | ||
87 | if (tmp_rate < rate) | ||
88 | tmp_diff = rate - tmp_rate; | ||
89 | else | ||
90 | tmp_diff = tmp_rate - rate; | ||
91 | |||
92 | if (best_diff < 0 || best_diff > tmp_diff) { | ||
93 | best_rate = tmp_rate; | ||
94 | best_diff = tmp_diff; | ||
95 | *best_parent_rate = tmp_parent_rate; | ||
96 | *best_parent_hw = __clk_get_hw(parent); | ||
97 | } | ||
98 | |||
99 | if (!best_diff || tmp_rate < rate) | ||
100 | break; | ||
101 | } | ||
69 | 102 | ||
70 | div = DIV_ROUND_CLOSEST(*parent_rate, rate); | 103 | if (!best_diff) |
71 | if (div > SAM9X5_USB_MAX_DIV + 1) | 104 | break; |
72 | div = SAM9X5_USB_MAX_DIV + 1; | 105 | } |
73 | 106 | ||
74 | return DIV_ROUND_CLOSEST(*parent_rate, div); | 107 | return best_rate; |
75 | } | 108 | } |
76 | 109 | ||
77 | static int at91sam9x5_clk_usb_set_parent(struct clk_hw *hw, u8 index) | 110 | 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, | |||
121 | 154 | ||
122 | static const struct clk_ops at91sam9x5_usb_ops = { | 155 | static const struct clk_ops at91sam9x5_usb_ops = { |
123 | .recalc_rate = at91sam9x5_clk_usb_recalc_rate, | 156 | .recalc_rate = at91sam9x5_clk_usb_recalc_rate, |
124 | .round_rate = at91sam9x5_clk_usb_round_rate, | 157 | .determine_rate = at91sam9x5_clk_usb_determine_rate, |
125 | .get_parent = at91sam9x5_clk_usb_get_parent, | 158 | .get_parent = at91sam9x5_clk_usb_get_parent, |
126 | .set_parent = at91sam9x5_clk_usb_set_parent, | 159 | .set_parent = at91sam9x5_clk_usb_set_parent, |
127 | .set_rate = at91sam9x5_clk_usb_set_rate, | 160 | .set_rate = at91sam9x5_clk_usb_set_rate, |
@@ -159,7 +192,7 @@ static const struct clk_ops at91sam9n12_usb_ops = { | |||
159 | .disable = at91sam9n12_clk_usb_disable, | 192 | .disable = at91sam9n12_clk_usb_disable, |
160 | .is_enabled = at91sam9n12_clk_usb_is_enabled, | 193 | .is_enabled = at91sam9n12_clk_usb_is_enabled, |
161 | .recalc_rate = at91sam9x5_clk_usb_recalc_rate, | 194 | .recalc_rate = at91sam9x5_clk_usb_recalc_rate, |
162 | .round_rate = at91sam9x5_clk_usb_round_rate, | 195 | .determine_rate = at91sam9x5_clk_usb_determine_rate, |
163 | .set_rate = at91sam9x5_clk_usb_set_rate, | 196 | .set_rate = at91sam9x5_clk_usb_set_rate, |
164 | }; | 197 | }; |
165 | 198 | ||
@@ -179,7 +212,8 @@ at91sam9x5_clk_register_usb(struct at91_pmc *pmc, const char *name, | |||
179 | init.ops = &at91sam9x5_usb_ops; | 212 | init.ops = &at91sam9x5_usb_ops; |
180 | init.parent_names = parent_names; | 213 | init.parent_names = parent_names; |
181 | init.num_parents = num_parents; | 214 | init.num_parents = num_parents; |
182 | init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; | 215 | init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | |
216 | CLK_SET_RATE_PARENT; | ||
183 | 217 | ||
184 | usb->hw.init = &init; | 218 | usb->hw.init = &init; |
185 | usb->pmc = pmc; | 219 | usb->pmc = pmc; |
@@ -207,7 +241,7 @@ at91sam9n12_clk_register_usb(struct at91_pmc *pmc, const char *name, | |||
207 | init.ops = &at91sam9n12_usb_ops; | 241 | init.ops = &at91sam9n12_usb_ops; |
208 | init.parent_names = &parent_name; | 242 | init.parent_names = &parent_name; |
209 | init.num_parents = 1; | 243 | init.num_parents = 1; |
210 | init.flags = CLK_SET_RATE_GATE; | 244 | init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; |
211 | 245 | ||
212 | usb->hw.init = &init; | 246 | usb->hw.init = &init; |
213 | usb->pmc = pmc; | 247 | usb->pmc = pmc; |