| .. | .. |
|---|
| 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; |
|---|