diff options
Diffstat (limited to 'drivers/clk/clk-divider.c')
| -rw-r--r-- | drivers/clk/clk-divider.c | 37 |
1 files changed, 36 insertions, 1 deletions
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index ec22112e569f..3fbee4540228 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c | |||
| @@ -144,6 +144,37 @@ static bool _is_valid_div(struct clk_divider *divider, unsigned int div) | |||
| 144 | return true; | 144 | return true; |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | static int _round_up_table(const struct clk_div_table *table, int div) | ||
| 148 | { | ||
| 149 | const struct clk_div_table *clkt; | ||
| 150 | int up = INT_MAX; | ||
| 151 | |||
| 152 | for (clkt = table; clkt->div; clkt++) { | ||
| 153 | if (clkt->div == div) | ||
| 154 | return clkt->div; | ||
| 155 | else if (clkt->div < div) | ||
| 156 | continue; | ||
| 157 | |||
| 158 | if ((clkt->div - div) < (up - div)) | ||
| 159 | up = clkt->div; | ||
| 160 | } | ||
| 161 | |||
| 162 | return up; | ||
| 163 | } | ||
| 164 | |||
| 165 | static int _div_round_up(struct clk_divider *divider, | ||
| 166 | unsigned long parent_rate, unsigned long rate) | ||
| 167 | { | ||
| 168 | int div = DIV_ROUND_UP(parent_rate, rate); | ||
| 169 | |||
| 170 | if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) | ||
| 171 | div = __roundup_pow_of_two(div); | ||
| 172 | if (divider->table) | ||
| 173 | div = _round_up_table(divider->table, div); | ||
| 174 | |||
| 175 | return div; | ||
| 176 | } | ||
| 177 | |||
| 147 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | 178 | static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, |
| 148 | unsigned long *best_parent_rate) | 179 | unsigned long *best_parent_rate) |
| 149 | { | 180 | { |
| @@ -159,7 +190,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, | |||
| 159 | 190 | ||
| 160 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { | 191 | if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) { |
| 161 | parent_rate = *best_parent_rate; | 192 | parent_rate = *best_parent_rate; |
| 162 | bestdiv = DIV_ROUND_UP(parent_rate, rate); | 193 | bestdiv = _div_round_up(divider, parent_rate, rate); |
| 163 | bestdiv = bestdiv == 0 ? 1 : bestdiv; | 194 | bestdiv = bestdiv == 0 ? 1 : bestdiv; |
| 164 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; | 195 | bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; |
| 165 | return bestdiv; | 196 | return bestdiv; |
| @@ -219,6 +250,10 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, | |||
| 219 | u32 val; | 250 | u32 val; |
| 220 | 251 | ||
| 221 | div = DIV_ROUND_UP(parent_rate, rate); | 252 | div = DIV_ROUND_UP(parent_rate, rate); |
| 253 | |||
| 254 | if (!_is_valid_div(divider, div)) | ||
| 255 | return -EINVAL; | ||
| 256 | |||
| 222 | value = _get_val(divider, div); | 257 | value = _get_val(divider, div); |
| 223 | 258 | ||
| 224 | if (value > div_mask(divider)) | 259 | if (value > div_mask(divider)) |
