1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * TI divider clock support 4 * 5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it> 6 * 7 * Loosely based on Linux kernel drivers/clk/ti/divider.c 8 */ 9 10#include <common.h> 11#include <clk.h> 12#include <clk-uclass.h> 13#include <div64.h> 14#include <dm.h> 15#include <dm/device_compat.h> 16#include <asm/io.h> 17#include <linux/clk-provider.h> 18#include <linux/kernel.h> 19#include <linux/log2.h> 20#include "clk.h" 21 22/* 23 * The reverse of DIV_ROUND_UP: The maximum number which 24 * divided by m is r 25 */ 26#define MULT_ROUND_UP(r, m) ((r) * (m) + (m) - 1) 27 28struct clk_ti_divider_priv { 29 struct clk parent; 30 struct clk_ti_reg reg; 31 const struct clk_div_table *table; 32 u8 shift; 33 u8 flags; 34 u8 div_flags; 35 s8 latch; 36 u16 min; 37 u16 max; 38 u16 mask; 39}; 40 41static unsigned int _get_div(const struct clk_div_table *table, ulong flags, 42 unsigned int val) 43{ 44 if (flags & CLK_DIVIDER_ONE_BASED) 45 return val; 46 47 if (flags & CLK_DIVIDER_POWER_OF_TWO) 48 return 1 << val; 49 50 if (table) 51 return clk_divider_get_table_div(table, val); 52 53 return val + 1; 54} 55 56static unsigned int _get_val(const struct clk_div_table *table, ulong flags, 57 unsigned int div) 58{ 59 if (flags & CLK_DIVIDER_ONE_BASED) 60 return div; 61 62 if (flags & CLK_DIVIDER_POWER_OF_TWO) 63 return __ffs(div); 64 65 if (table) 66 return clk_divider_get_table_val(table, div); 67 68 return div - 1; 69} 70 71static int _div_round_up(const struct clk_div_table *table, ulong parent_rate, 72 ulong rate) 73{ 74 const struct clk_div_table *clkt; 75 int up = INT_MAX; 76 int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); 77 78 for (clkt = table; clkt->div; clkt++) { 79 if (clkt->div == div) 80 return clkt->div; 81 else if (clkt->div < div) 82 continue; 83 84 if ((clkt->div - div) < (up - div)) 85 up = clkt->div; 86 } 87 88 return up; 89} 90 91static int _div_round(const struct clk_div_table *table, ulong parent_rate, 92 ulong rate) 93{ 94 if (table) 95 return _div_round_up(table, parent_rate, rate); 96 97 return DIV_ROUND_UP(parent_rate, rate); 98} 99 100static int clk_ti_divider_best_div(struct clk *clk, ulong rate, 101 ulong *best_parent_rate) 102{ 103 struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev); 104 ulong parent_rate, parent_round_rate, max_div; 105 ulong best_rate, r; 106 int i, best_div = 0; 107 108 parent_rate = clk_get_rate(&priv->parent); 109 if (IS_ERR_VALUE(parent_rate)) 110 return parent_rate; 111 112 if (!rate) 113 rate = 1; 114 115 if (!(clk->flags & CLK_SET_RATE_PARENT)) { 116 best_div = _div_round(priv->table, parent_rate, rate); 117 if (best_div == 0) 118 best_div = 1; 119 120 if (best_div > priv->max) 121 best_div = priv->max; 122 123 *best_parent_rate = parent_rate; 124 return best_div; 125 } 126 127 max_div = min(ULONG_MAX / rate, (ulong)priv->max); 128 for (best_rate = 0, i = 1; i <= max_div; i++) { 129 if (!clk_divider_is_valid_div(priv->table, priv->div_flags, i)) 130 continue; 131 132 /* 133 * It's the most ideal case if the requested rate can be 134 * divided from parent clock without needing to change 135 * parent rate, so return the divider immediately. 136 */ 137 if ((rate * i) == parent_rate) { 138 *best_parent_rate = parent_rate; 139 dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, div=%d\n", 140 rate, rate, i); 141 return i; 142 } 143 144 parent_round_rate = clk_round_rate(&priv->parent, 145 MULT_ROUND_UP(rate, i)); 146 if (IS_ERR_VALUE(parent_round_rate)) 147 continue; 148 149 r = DIV_ROUND_UP(parent_round_rate, i); 150 if (r <= rate && r > best_rate) { 151 best_div = i; 152 best_rate = r; 153 *best_parent_rate = parent_round_rate; 154 if (best_rate == rate) 155 break; 156 } 157 } 158 159 if (best_div == 0) { 160 best_div = priv->max; 161 parent_round_rate = clk_round_rate(&priv->parent, 1); 162 if (IS_ERR_VALUE(parent_round_rate)) 163 return parent_round_rate; 164 } 165 166 dev_dbg(clk->dev, "rate=%ld, best_rate=%ld, div=%d\n", rate, best_rate, 167 best_div); 168 169 return best_div; 170} 171 172static ulong clk_ti_divider_round_rate(struct clk *clk, ulong rate) 173{ 174 ulong parent_rate; 175 int div; 176 177 div = clk_ti_divider_best_div(clk, rate, &parent_rate); 178 if (div < 0) 179 return div; 180 181 return DIV_ROUND_UP(parent_rate, div); 182} 183 184static ulong clk_ti_divider_set_rate(struct clk *clk, ulong rate) 185{ 186 struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev); 187 ulong parent_rate; 188 int div; 189 u32 val, v; 190 191 div = clk_ti_divider_best_div(clk, rate, &parent_rate); 192 if (div < 0) 193 return div; 194 195 if (clk->flags & CLK_SET_RATE_PARENT) { 196 parent_rate = clk_set_rate(&priv->parent, parent_rate); 197 if (IS_ERR_VALUE(parent_rate)) 198 return parent_rate; 199 } 200 201 val = _get_val(priv->table, priv->div_flags, div); 202 203 v = clk_ti_readl(&priv->reg); 204 v &= ~(priv->mask << priv->shift); 205 v |= val << priv->shift; 206 clk_ti_writel(v, &priv->reg); 207 clk_ti_latch(&priv->reg, priv->latch); 208 209 return clk_get_rate(clk); 210} 211 212static ulong clk_ti_divider_get_rate(struct clk *clk) 213{ 214 struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev); 215 ulong rate, parent_rate; 216 unsigned int div; 217 u32 v; 218 219 parent_rate = clk_get_rate(&priv->parent); 220 if (IS_ERR_VALUE(parent_rate)) 221 return parent_rate; 222 223 v = clk_ti_readl(&priv->reg) >> priv->shift; 224 v &= priv->mask; 225 226 div = _get_div(priv->table, priv->div_flags, v); 227 if (!div) { 228 if (!(priv->div_flags & CLK_DIVIDER_ALLOW_ZERO)) 229 dev_warn(clk->dev, 230 "zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n"); 231 return parent_rate; 232 } 233 234 rate = DIV_ROUND_UP(parent_rate, div); 235 dev_dbg(clk->dev, "rate=%ld\n", rate); 236 return rate; 237} 238 239static int clk_ti_divider_request(struct clk *clk) 240{ 241 struct clk_ti_divider_priv *priv = dev_get_priv(clk->dev); 242 243 clk->flags = priv->flags; 244 return 0; 245} 246 247const struct clk_ops clk_ti_divider_ops = { 248 .request = clk_ti_divider_request, 249 .round_rate = clk_ti_divider_round_rate, 250 .get_rate = clk_ti_divider_get_rate, 251 .set_rate = clk_ti_divider_set_rate 252}; 253 254static int clk_ti_divider_remove(struct udevice *dev) 255{ 256 struct clk_ti_divider_priv *priv = dev_get_priv(dev); 257 int err; 258 259 err = clk_release_all(&priv->parent, 1); 260 if (err) { 261 dev_err(dev, "failed to release parent clock\n"); 262 return err; 263 } 264 265 return 0; 266} 267 268static int clk_ti_divider_probe(struct udevice *dev) 269{ 270 struct clk_ti_divider_priv *priv = dev_get_priv(dev); 271 int err; 272 273 err = clk_get_by_index(dev, 0, &priv->parent); 274 if (err) { 275 dev_err(dev, "failed to get parent clock\n"); 276 return err; 277 } 278 279 return 0; 280} 281 282static int clk_ti_divider_of_to_plat(struct udevice *dev) 283{ 284 struct clk_ti_divider_priv *priv = dev_get_priv(dev); 285 struct clk_div_table *table = NULL; 286 u32 val, valid_div; 287 u32 min_div = 0; 288 u32 max_val, max_div = 0; 289 u16 mask; 290 int i, div_num, err; 291 292 err = clk_ti_get_reg_addr(dev, 0, &priv->reg); 293 if (err) { 294 dev_err(dev, "failed to get register address\n"); 295 return err; 296 } 297 298 priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0); 299 priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL); 300 if (dev_read_bool(dev, "ti,index-starts-at-one")) 301 priv->div_flags |= CLK_DIVIDER_ONE_BASED; 302 303 if (dev_read_bool(dev, "ti,index-power-of-two")) 304 priv->div_flags |= CLK_DIVIDER_POWER_OF_TWO; 305 306 if (dev_read_bool(dev, "ti,set-rate-parent")) 307 priv->flags |= CLK_SET_RATE_PARENT; 308 309 if (dev_read_prop(dev, "ti,dividers", &div_num)) { 310 div_num /= sizeof(u32); 311 312 /* Determine required size for divider table */ 313 for (i = 0, valid_div = 0; i < div_num; i++) { 314 dev_read_u32_index(dev, "ti,dividers", i, &val); 315 if (val) 316 valid_div++; 317 } 318 319 if (!valid_div) { 320 dev_err(dev, "no valid dividers\n"); 321 return -EINVAL; 322 } 323 324 table = calloc(valid_div + 1, sizeof(*table)); 325 if (!table) 326 return -ENOMEM; 327 328 for (i = 0, valid_div = 0; i < div_num; i++) { 329 dev_read_u32_index(dev, "ti,dividers", i, &val); 330 if (!val) 331 continue; 332 333 table[valid_div].div = val; 334 table[valid_div].val = i; 335 valid_div++; 336 if (val > max_div) 337 max_div = val; 338 339 if (!min_div || val < min_div) 340 min_div = val; 341 } 342 343 max_val = max_div; 344 } else { 345 /* Divider table not provided, determine min/max divs */ 346 min_div = dev_read_u32_default(dev, "ti,min-div", 1); 347 if (dev_read_u32(dev, "ti,max-div", &max_div)) { 348 dev_err(dev, "missing 'max-div' property\n"); 349 return -EFAULT; 350 } 351 352 max_val = max_div; 353 if (!(priv->div_flags & CLK_DIVIDER_ONE_BASED) && 354 !(priv->div_flags & CLK_DIVIDER_POWER_OF_TWO)) 355 max_val--; 356 } 357 358 priv->table = table; 359 priv->min = min_div; 360 priv->max = max_div; 361 362 if (priv->div_flags & CLK_DIVIDER_POWER_OF_TWO) 363 mask = fls(max_val) - 1; 364 else 365 mask = max_val; 366 367 priv->mask = (1 << fls(mask)) - 1; 368 return 0; 369} 370 371static const struct udevice_id clk_ti_divider_of_match[] = { 372 {.compatible = "ti,divider-clock"}, 373 {} 374}; 375 376U_BOOT_DRIVER(clk_ti_divider) = { 377 .name = "ti_divider_clock", 378 .id = UCLASS_CLK, 379 .of_match = clk_ti_divider_of_match, 380 .of_to_plat = clk_ti_divider_of_to_plat, 381 .probe = clk_ti_divider_probe, 382 .remove = clk_ti_divider_remove, 383 .priv_auto = sizeof(struct clk_ti_divider_priv), 384 .ops = &clk_ti_divider_ops, 385}; 386