.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2016 Maxime Ripard |
---|
3 | 4 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
---|
4 | | - * |
---|
5 | | - * This program is free software; you can redistribute it and/or |
---|
6 | | - * modify it under the terms of the GNU General Public License as |
---|
7 | | - * published by the Free Software Foundation; either version 2 of |
---|
8 | | - * the License, or (at your option) any later version. |
---|
9 | 5 | */ |
---|
10 | 6 | |
---|
11 | 7 | #include <linux/clk-provider.h> |
---|
| 8 | +#include <linux/io.h> |
---|
12 | 9 | |
---|
13 | 10 | #include "ccu_gate.h" |
---|
14 | 11 | #include "ccu_mp.h" |
---|
.. | .. |
---|
40 | 37 | *p = best_p; |
---|
41 | 38 | } |
---|
42 | 39 | |
---|
| 40 | +static unsigned long ccu_mp_find_best_with_parent_adj(struct clk_hw *hw, |
---|
| 41 | + unsigned long *parent, |
---|
| 42 | + unsigned long rate, |
---|
| 43 | + unsigned int max_m, |
---|
| 44 | + unsigned int max_p) |
---|
| 45 | +{ |
---|
| 46 | + unsigned long parent_rate_saved; |
---|
| 47 | + unsigned long parent_rate, now; |
---|
| 48 | + unsigned long best_rate = 0; |
---|
| 49 | + unsigned int _m, _p, div; |
---|
| 50 | + unsigned long maxdiv; |
---|
| 51 | + |
---|
| 52 | + parent_rate_saved = *parent; |
---|
| 53 | + |
---|
| 54 | + /* |
---|
| 55 | + * The maximum divider we can use without overflowing |
---|
| 56 | + * unsigned long in rate * m * p below |
---|
| 57 | + */ |
---|
| 58 | + maxdiv = max_m * max_p; |
---|
| 59 | + maxdiv = min(ULONG_MAX / rate, maxdiv); |
---|
| 60 | + |
---|
| 61 | + for (_p = 1; _p <= max_p; _p <<= 1) { |
---|
| 62 | + for (_m = 1; _m <= max_m; _m++) { |
---|
| 63 | + div = _m * _p; |
---|
| 64 | + |
---|
| 65 | + if (div > maxdiv) |
---|
| 66 | + break; |
---|
| 67 | + |
---|
| 68 | + if (rate * div == parent_rate_saved) { |
---|
| 69 | + /* |
---|
| 70 | + * It's the most ideal case if the requested |
---|
| 71 | + * rate can be divided from parent clock without |
---|
| 72 | + * needing to change parent rate, so return the |
---|
| 73 | + * divider immediately. |
---|
| 74 | + */ |
---|
| 75 | + *parent = parent_rate_saved; |
---|
| 76 | + return rate; |
---|
| 77 | + } |
---|
| 78 | + |
---|
| 79 | + parent_rate = clk_hw_round_rate(hw, rate * div); |
---|
| 80 | + now = parent_rate / div; |
---|
| 81 | + |
---|
| 82 | + if (now <= rate && now > best_rate) { |
---|
| 83 | + best_rate = now; |
---|
| 84 | + *parent = parent_rate; |
---|
| 85 | + |
---|
| 86 | + if (now == rate) |
---|
| 87 | + return rate; |
---|
| 88 | + } |
---|
| 89 | + } |
---|
| 90 | + } |
---|
| 91 | + |
---|
| 92 | + return best_rate; |
---|
| 93 | +} |
---|
| 94 | + |
---|
43 | 95 | static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux, |
---|
44 | 96 | struct clk_hw *hw, |
---|
45 | 97 | unsigned long *parent_rate, |
---|
.. | .. |
---|
56 | 108 | max_m = cmp->m.max ?: 1 << cmp->m.width; |
---|
57 | 109 | max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1); |
---|
58 | 110 | |
---|
59 | | - ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); |
---|
60 | | - rate = *parent_rate / p / m; |
---|
| 111 | + if (!clk_hw_can_set_rate_parent(&cmp->common.hw)) { |
---|
| 112 | + ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p); |
---|
| 113 | + rate = *parent_rate / p / m; |
---|
| 114 | + } else { |
---|
| 115 | + rate = ccu_mp_find_best_with_parent_adj(hw, parent_rate, rate, |
---|
| 116 | + max_m, max_p); |
---|
| 117 | + } |
---|
61 | 118 | |
---|
62 | 119 | if (cmp->common.features & CCU_FEATURE_FIXED_POSTDIV) |
---|
63 | 120 | rate /= cmp->fixed_post_div; |
---|