162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> 462306a36Sopenharmony_ci * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> 562306a36Sopenharmony_ci * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Adjustable divider clock implementation 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk-provider.h> 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/string.h> 1762306a36Sopenharmony_ci#include <linux/log2.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * DOC: basic adjustable divider clock that cannot gate 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Traits of this clock: 2362306a36Sopenharmony_ci * prepare - clk_prepare only ensures that parents are prepared 2462306a36Sopenharmony_ci * enable - clk_enable only ensures that parents are enabled 2562306a36Sopenharmony_ci * rate - rate is adjustable. clk->rate = ceiling(parent->rate / divisor) 2662306a36Sopenharmony_ci * parent - fixed parent. No clk_set_parent support 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic inline u32 clk_div_readl(struct clk_divider *divider) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (divider->flags & CLK_DIVIDER_BIG_ENDIAN) 3262306a36Sopenharmony_ci return ioread32be(divider->reg); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci return readl(divider->reg); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline void clk_div_writel(struct clk_divider *divider, u32 val) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if (divider->flags & CLK_DIVIDER_BIG_ENDIAN) 4062306a36Sopenharmony_ci iowrite32be(val, divider->reg); 4162306a36Sopenharmony_ci else 4262306a36Sopenharmony_ci writel(val, divider->reg); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic unsigned int _get_table_maxdiv(const struct clk_div_table *table, 4662306a36Sopenharmony_ci u8 width) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci unsigned int maxdiv = 0, mask = clk_div_mask(width); 4962306a36Sopenharmony_ci const struct clk_div_table *clkt; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci for (clkt = table; clkt->div; clkt++) 5262306a36Sopenharmony_ci if (clkt->div > maxdiv && clkt->val <= mask) 5362306a36Sopenharmony_ci maxdiv = clkt->div; 5462306a36Sopenharmony_ci return maxdiv; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic unsigned int _get_table_mindiv(const struct clk_div_table *table) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci unsigned int mindiv = UINT_MAX; 6062306a36Sopenharmony_ci const struct clk_div_table *clkt; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci for (clkt = table; clkt->div; clkt++) 6362306a36Sopenharmony_ci if (clkt->div < mindiv) 6462306a36Sopenharmony_ci mindiv = clkt->div; 6562306a36Sopenharmony_ci return mindiv; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic unsigned int _get_maxdiv(const struct clk_div_table *table, u8 width, 6962306a36Sopenharmony_ci unsigned long flags) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci if (flags & CLK_DIVIDER_ONE_BASED) 7262306a36Sopenharmony_ci return clk_div_mask(width); 7362306a36Sopenharmony_ci if (flags & CLK_DIVIDER_POWER_OF_TWO) 7462306a36Sopenharmony_ci return 1 << clk_div_mask(width); 7562306a36Sopenharmony_ci if (table) 7662306a36Sopenharmony_ci return _get_table_maxdiv(table, width); 7762306a36Sopenharmony_ci return clk_div_mask(width) + 1; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic unsigned int _get_table_div(const struct clk_div_table *table, 8162306a36Sopenharmony_ci unsigned int val) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci const struct clk_div_table *clkt; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci for (clkt = table; clkt->div; clkt++) 8662306a36Sopenharmony_ci if (clkt->val == val) 8762306a36Sopenharmony_ci return clkt->div; 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic unsigned int _get_div(const struct clk_div_table *table, 9262306a36Sopenharmony_ci unsigned int val, unsigned long flags, u8 width) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci if (flags & CLK_DIVIDER_ONE_BASED) 9562306a36Sopenharmony_ci return val; 9662306a36Sopenharmony_ci if (flags & CLK_DIVIDER_POWER_OF_TWO) 9762306a36Sopenharmony_ci return 1 << val; 9862306a36Sopenharmony_ci if (flags & CLK_DIVIDER_MAX_AT_ZERO) 9962306a36Sopenharmony_ci return val ? val : clk_div_mask(width) + 1; 10062306a36Sopenharmony_ci if (table) 10162306a36Sopenharmony_ci return _get_table_div(table, val); 10262306a36Sopenharmony_ci return val + 1; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic unsigned int _get_table_val(const struct clk_div_table *table, 10662306a36Sopenharmony_ci unsigned int div) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci const struct clk_div_table *clkt; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (clkt = table; clkt->div; clkt++) 11162306a36Sopenharmony_ci if (clkt->div == div) 11262306a36Sopenharmony_ci return clkt->val; 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic unsigned int _get_val(const struct clk_div_table *table, 11762306a36Sopenharmony_ci unsigned int div, unsigned long flags, u8 width) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci if (flags & CLK_DIVIDER_ONE_BASED) 12062306a36Sopenharmony_ci return div; 12162306a36Sopenharmony_ci if (flags & CLK_DIVIDER_POWER_OF_TWO) 12262306a36Sopenharmony_ci return __ffs(div); 12362306a36Sopenharmony_ci if (flags & CLK_DIVIDER_MAX_AT_ZERO) 12462306a36Sopenharmony_ci return (div == clk_div_mask(width) + 1) ? 0 : div; 12562306a36Sopenharmony_ci if (table) 12662306a36Sopenharmony_ci return _get_table_val(table, div); 12762306a36Sopenharmony_ci return div - 1; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciunsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, 13162306a36Sopenharmony_ci unsigned int val, 13262306a36Sopenharmony_ci const struct clk_div_table *table, 13362306a36Sopenharmony_ci unsigned long flags, unsigned long width) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned int div; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci div = _get_div(table, val, flags, width); 13862306a36Sopenharmony_ci if (!div) { 13962306a36Sopenharmony_ci WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO), 14062306a36Sopenharmony_ci "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", 14162306a36Sopenharmony_ci clk_hw_get_name(hw)); 14262306a36Sopenharmony_ci return parent_rate; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return DIV_ROUND_UP_ULL((u64)parent_rate, div); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(divider_recalc_rate); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic unsigned long clk_divider_recalc_rate(struct clk_hw *hw, 15062306a36Sopenharmony_ci unsigned long parent_rate) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 15362306a36Sopenharmony_ci unsigned int val; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci val = clk_div_readl(divider) >> divider->shift; 15662306a36Sopenharmony_ci val &= clk_div_mask(divider->width); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return divider_recalc_rate(hw, parent_rate, val, divider->table, 15962306a36Sopenharmony_ci divider->flags, divider->width); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic bool _is_valid_table_div(const struct clk_div_table *table, 16362306a36Sopenharmony_ci unsigned int div) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci const struct clk_div_table *clkt; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (clkt = table; clkt->div; clkt++) 16862306a36Sopenharmony_ci if (clkt->div == div) 16962306a36Sopenharmony_ci return true; 17062306a36Sopenharmony_ci return false; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic bool _is_valid_div(const struct clk_div_table *table, unsigned int div, 17462306a36Sopenharmony_ci unsigned long flags) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (flags & CLK_DIVIDER_POWER_OF_TWO) 17762306a36Sopenharmony_ci return is_power_of_2(div); 17862306a36Sopenharmony_ci if (table) 17962306a36Sopenharmony_ci return _is_valid_table_div(table, div); 18062306a36Sopenharmony_ci return true; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int _round_up_table(const struct clk_div_table *table, int div) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci const struct clk_div_table *clkt; 18662306a36Sopenharmony_ci int up = INT_MAX; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (clkt = table; clkt->div; clkt++) { 18962306a36Sopenharmony_ci if (clkt->div == div) 19062306a36Sopenharmony_ci return clkt->div; 19162306a36Sopenharmony_ci else if (clkt->div < div) 19262306a36Sopenharmony_ci continue; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if ((clkt->div - div) < (up - div)) 19562306a36Sopenharmony_ci up = clkt->div; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return up; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int _round_down_table(const struct clk_div_table *table, int div) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci const struct clk_div_table *clkt; 20462306a36Sopenharmony_ci int down = _get_table_mindiv(table); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci for (clkt = table; clkt->div; clkt++) { 20762306a36Sopenharmony_ci if (clkt->div == div) 20862306a36Sopenharmony_ci return clkt->div; 20962306a36Sopenharmony_ci else if (clkt->div > div) 21062306a36Sopenharmony_ci continue; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if ((div - clkt->div) < (div - down)) 21362306a36Sopenharmony_ci down = clkt->div; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return down; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int _div_round_up(const struct clk_div_table *table, 22062306a36Sopenharmony_ci unsigned long parent_rate, unsigned long rate, 22162306a36Sopenharmony_ci unsigned long flags) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (flags & CLK_DIVIDER_POWER_OF_TWO) 22662306a36Sopenharmony_ci div = __roundup_pow_of_two(div); 22762306a36Sopenharmony_ci if (table) 22862306a36Sopenharmony_ci div = _round_up_table(table, div); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return div; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int _div_round_closest(const struct clk_div_table *table, 23462306a36Sopenharmony_ci unsigned long parent_rate, unsigned long rate, 23562306a36Sopenharmony_ci unsigned long flags) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci int up, down; 23862306a36Sopenharmony_ci unsigned long up_rate, down_rate; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); 24162306a36Sopenharmony_ci down = parent_rate / rate; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (flags & CLK_DIVIDER_POWER_OF_TWO) { 24462306a36Sopenharmony_ci up = __roundup_pow_of_two(up); 24562306a36Sopenharmony_ci down = __rounddown_pow_of_two(down); 24662306a36Sopenharmony_ci } else if (table) { 24762306a36Sopenharmony_ci up = _round_up_table(table, up); 24862306a36Sopenharmony_ci down = _round_down_table(table, down); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci up_rate = DIV_ROUND_UP_ULL((u64)parent_rate, up); 25262306a36Sopenharmony_ci down_rate = DIV_ROUND_UP_ULL((u64)parent_rate, down); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return (rate - up_rate) <= (down_rate - rate) ? up : down; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int _div_round(const struct clk_div_table *table, 25862306a36Sopenharmony_ci unsigned long parent_rate, unsigned long rate, 25962306a36Sopenharmony_ci unsigned long flags) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci if (flags & CLK_DIVIDER_ROUND_CLOSEST) 26262306a36Sopenharmony_ci return _div_round_closest(table, parent_rate, rate, flags); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return _div_round_up(table, parent_rate, rate, flags); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic bool _is_best_div(unsigned long rate, unsigned long now, 26862306a36Sopenharmony_ci unsigned long best, unsigned long flags) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci if (flags & CLK_DIVIDER_ROUND_CLOSEST) 27162306a36Sopenharmony_ci return abs(rate - now) < abs(rate - best); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return now <= rate && now > best; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int _next_div(const struct clk_div_table *table, int div, 27762306a36Sopenharmony_ci unsigned long flags) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci div++; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (flags & CLK_DIVIDER_POWER_OF_TWO) 28262306a36Sopenharmony_ci return __roundup_pow_of_two(div); 28362306a36Sopenharmony_ci if (table) 28462306a36Sopenharmony_ci return _round_up_table(table, div); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return div; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent, 29062306a36Sopenharmony_ci unsigned long rate, 29162306a36Sopenharmony_ci unsigned long *best_parent_rate, 29262306a36Sopenharmony_ci const struct clk_div_table *table, u8 width, 29362306a36Sopenharmony_ci unsigned long flags) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci int i, bestdiv = 0; 29662306a36Sopenharmony_ci unsigned long parent_rate, best = 0, now, maxdiv; 29762306a36Sopenharmony_ci unsigned long parent_rate_saved = *best_parent_rate; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!rate) 30062306a36Sopenharmony_ci rate = 1; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci maxdiv = _get_maxdiv(table, width, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { 30562306a36Sopenharmony_ci parent_rate = *best_parent_rate; 30662306a36Sopenharmony_ci bestdiv = _div_round(table, parent_rate, rate, flags); 30762306a36Sopenharmony_ci bestdiv = bestdiv == 0 ? 1 : bestdiv; 30862306a36Sopenharmony_ci bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; 30962306a36Sopenharmony_ci return bestdiv; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * The maximum divider we can use without overflowing 31462306a36Sopenharmony_ci * unsigned long in rate * i below 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci maxdiv = min(ULONG_MAX / rate, maxdiv); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci for (i = _next_div(table, 0, flags); i <= maxdiv; 31962306a36Sopenharmony_ci i = _next_div(table, i, flags)) { 32062306a36Sopenharmony_ci if (rate * i == parent_rate_saved) { 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * It's the most ideal case if the requested rate can be 32362306a36Sopenharmony_ci * divided from parent clock without needing to change 32462306a36Sopenharmony_ci * parent rate, so return the divider immediately. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci *best_parent_rate = parent_rate_saved; 32762306a36Sopenharmony_ci return i; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci parent_rate = clk_hw_round_rate(parent, rate * i); 33062306a36Sopenharmony_ci now = DIV_ROUND_UP_ULL((u64)parent_rate, i); 33162306a36Sopenharmony_ci if (_is_best_div(rate, now, best, flags)) { 33262306a36Sopenharmony_ci bestdiv = i; 33362306a36Sopenharmony_ci best = now; 33462306a36Sopenharmony_ci *best_parent_rate = parent_rate; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!bestdiv) { 33962306a36Sopenharmony_ci bestdiv = _get_maxdiv(table, width, flags); 34062306a36Sopenharmony_ci *best_parent_rate = clk_hw_round_rate(parent, 1); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return bestdiv; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ciint divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req, 34762306a36Sopenharmony_ci const struct clk_div_table *table, u8 width, 34862306a36Sopenharmony_ci unsigned long flags) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci int div; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci div = clk_divider_bestdiv(hw, req->best_parent_hw, req->rate, 35362306a36Sopenharmony_ci &req->best_parent_rate, table, width, flags); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(divider_determine_rate); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ciint divider_ro_determine_rate(struct clk_hw *hw, struct clk_rate_request *req, 36262306a36Sopenharmony_ci const struct clk_div_table *table, u8 width, 36362306a36Sopenharmony_ci unsigned long flags, unsigned int val) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci int div; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci div = _get_div(table, val, flags, width); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Even a read-only clock can propagate a rate change */ 37062306a36Sopenharmony_ci if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { 37162306a36Sopenharmony_ci if (!req->best_parent_hw) 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw, 37562306a36Sopenharmony_ci req->rate * div); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci req->rate = DIV_ROUND_UP_ULL((u64)req->best_parent_rate, div); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(divider_ro_determine_rate); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cilong divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent, 38562306a36Sopenharmony_ci unsigned long rate, unsigned long *prate, 38662306a36Sopenharmony_ci const struct clk_div_table *table, 38762306a36Sopenharmony_ci u8 width, unsigned long flags) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct clk_rate_request req; 39062306a36Sopenharmony_ci int ret; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci clk_hw_init_rate_request(hw, &req, rate); 39362306a36Sopenharmony_ci req.best_parent_rate = *prate; 39462306a36Sopenharmony_ci req.best_parent_hw = parent; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ret = divider_determine_rate(hw, &req, table, width, flags); 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci *prate = req.best_parent_rate; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return req.rate; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(divider_round_rate_parent); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cilong divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent, 40762306a36Sopenharmony_ci unsigned long rate, unsigned long *prate, 40862306a36Sopenharmony_ci const struct clk_div_table *table, u8 width, 40962306a36Sopenharmony_ci unsigned long flags, unsigned int val) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct clk_rate_request req; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci clk_hw_init_rate_request(hw, &req, rate); 41562306a36Sopenharmony_ci req.best_parent_rate = *prate; 41662306a36Sopenharmony_ci req.best_parent_hw = parent; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci ret = divider_ro_determine_rate(hw, &req, table, width, flags, val); 41962306a36Sopenharmony_ci if (ret) 42062306a36Sopenharmony_ci return ret; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci *prate = req.best_parent_rate; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return req.rate; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(divider_ro_round_rate_parent); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, 42962306a36Sopenharmony_ci unsigned long *prate) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* if read only, just return current value */ 43462306a36Sopenharmony_ci if (divider->flags & CLK_DIVIDER_READ_ONLY) { 43562306a36Sopenharmony_ci u32 val; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci val = clk_div_readl(divider) >> divider->shift; 43862306a36Sopenharmony_ci val &= clk_div_mask(divider->width); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return divider_ro_round_rate(hw, rate, prate, divider->table, 44162306a36Sopenharmony_ci divider->width, divider->flags, 44262306a36Sopenharmony_ci val); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return divider_round_rate(hw, rate, prate, divider->table, 44662306a36Sopenharmony_ci divider->width, divider->flags); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int clk_divider_determine_rate(struct clk_hw *hw, 45062306a36Sopenharmony_ci struct clk_rate_request *req) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci /* if read only, just return current value */ 45562306a36Sopenharmony_ci if (divider->flags & CLK_DIVIDER_READ_ONLY) { 45662306a36Sopenharmony_ci u32 val; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci val = clk_div_readl(divider) >> divider->shift; 45962306a36Sopenharmony_ci val &= clk_div_mask(divider->width); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return divider_ro_determine_rate(hw, req, divider->table, 46262306a36Sopenharmony_ci divider->width, 46362306a36Sopenharmony_ci divider->flags, val); 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return divider_determine_rate(hw, req, divider->table, divider->width, 46762306a36Sopenharmony_ci divider->flags); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciint divider_get_val(unsigned long rate, unsigned long parent_rate, 47162306a36Sopenharmony_ci const struct clk_div_table *table, u8 width, 47262306a36Sopenharmony_ci unsigned long flags) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci unsigned int div, value; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (!_is_valid_div(table, div, flags)) 47962306a36Sopenharmony_ci return -EINVAL; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci value = _get_val(table, div, flags, width); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return min_t(unsigned int, value, clk_div_mask(width)); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(divider_get_val); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate, 48862306a36Sopenharmony_ci unsigned long parent_rate) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 49162306a36Sopenharmony_ci int value; 49262306a36Sopenharmony_ci unsigned long flags = 0; 49362306a36Sopenharmony_ci u32 val; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci value = divider_get_val(rate, parent_rate, divider->table, 49662306a36Sopenharmony_ci divider->width, divider->flags); 49762306a36Sopenharmony_ci if (value < 0) 49862306a36Sopenharmony_ci return value; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (divider->lock) 50162306a36Sopenharmony_ci spin_lock_irqsave(divider->lock, flags); 50262306a36Sopenharmony_ci else 50362306a36Sopenharmony_ci __acquire(divider->lock); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { 50662306a36Sopenharmony_ci val = clk_div_mask(divider->width) << (divider->shift + 16); 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci val = clk_div_readl(divider); 50962306a36Sopenharmony_ci val &= ~(clk_div_mask(divider->width) << divider->shift); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci val |= (u32)value << divider->shift; 51262306a36Sopenharmony_ci clk_div_writel(divider, val); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (divider->lock) 51562306a36Sopenharmony_ci spin_unlock_irqrestore(divider->lock, flags); 51662306a36Sopenharmony_ci else 51762306a36Sopenharmony_ci __release(divider->lock); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci return 0; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciconst struct clk_ops clk_divider_ops = { 52362306a36Sopenharmony_ci .recalc_rate = clk_divider_recalc_rate, 52462306a36Sopenharmony_ci .round_rate = clk_divider_round_rate, 52562306a36Sopenharmony_ci .determine_rate = clk_divider_determine_rate, 52662306a36Sopenharmony_ci .set_rate = clk_divider_set_rate, 52762306a36Sopenharmony_ci}; 52862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_divider_ops); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ciconst struct clk_ops clk_divider_ro_ops = { 53162306a36Sopenharmony_ci .recalc_rate = clk_divider_recalc_rate, 53262306a36Sopenharmony_ci .round_rate = clk_divider_round_rate, 53362306a36Sopenharmony_ci .determine_rate = clk_divider_determine_rate, 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_divider_ro_ops); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cistruct clk_hw *__clk_hw_register_divider(struct device *dev, 53862306a36Sopenharmony_ci struct device_node *np, const char *name, 53962306a36Sopenharmony_ci const char *parent_name, const struct clk_hw *parent_hw, 54062306a36Sopenharmony_ci const struct clk_parent_data *parent_data, unsigned long flags, 54162306a36Sopenharmony_ci void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, 54262306a36Sopenharmony_ci const struct clk_div_table *table, spinlock_t *lock) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct clk_divider *div; 54562306a36Sopenharmony_ci struct clk_hw *hw; 54662306a36Sopenharmony_ci struct clk_init_data init = {}; 54762306a36Sopenharmony_ci int ret; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) { 55062306a36Sopenharmony_ci if (width + shift > 16) { 55162306a36Sopenharmony_ci pr_warn("divider value exceeds LOWORD field\n"); 55262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* allocate the divider */ 55762306a36Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 55862306a36Sopenharmony_ci if (!div) 55962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci init.name = name; 56262306a36Sopenharmony_ci if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) 56362306a36Sopenharmony_ci init.ops = &clk_divider_ro_ops; 56462306a36Sopenharmony_ci else 56562306a36Sopenharmony_ci init.ops = &clk_divider_ops; 56662306a36Sopenharmony_ci init.flags = flags; 56762306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 56862306a36Sopenharmony_ci init.parent_hws = parent_hw ? &parent_hw : NULL; 56962306a36Sopenharmony_ci init.parent_data = parent_data; 57062306a36Sopenharmony_ci if (parent_name || parent_hw || parent_data) 57162306a36Sopenharmony_ci init.num_parents = 1; 57262306a36Sopenharmony_ci else 57362306a36Sopenharmony_ci init.num_parents = 0; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* struct clk_divider assignments */ 57662306a36Sopenharmony_ci div->reg = reg; 57762306a36Sopenharmony_ci div->shift = shift; 57862306a36Sopenharmony_ci div->width = width; 57962306a36Sopenharmony_ci div->flags = clk_divider_flags; 58062306a36Sopenharmony_ci div->lock = lock; 58162306a36Sopenharmony_ci div->hw.init = &init; 58262306a36Sopenharmony_ci div->table = table; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* register the clock */ 58562306a36Sopenharmony_ci hw = &div->hw; 58662306a36Sopenharmony_ci ret = clk_hw_register(dev, hw); 58762306a36Sopenharmony_ci if (ret) { 58862306a36Sopenharmony_ci kfree(div); 58962306a36Sopenharmony_ci hw = ERR_PTR(ret); 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci return hw; 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__clk_hw_register_divider); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci/** 59762306a36Sopenharmony_ci * clk_register_divider_table - register a table based divider clock with 59862306a36Sopenharmony_ci * the clock framework 59962306a36Sopenharmony_ci * @dev: device registering this clock 60062306a36Sopenharmony_ci * @name: name of this clock 60162306a36Sopenharmony_ci * @parent_name: name of clock's parent 60262306a36Sopenharmony_ci * @flags: framework-specific flags 60362306a36Sopenharmony_ci * @reg: register address to adjust divider 60462306a36Sopenharmony_ci * @shift: number of bits to shift the bitfield 60562306a36Sopenharmony_ci * @width: width of the bitfield 60662306a36Sopenharmony_ci * @clk_divider_flags: divider-specific flags for this clock 60762306a36Sopenharmony_ci * @table: array of divider/value pairs ending with a div set to 0 60862306a36Sopenharmony_ci * @lock: shared register lock for this clock 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_cistruct clk *clk_register_divider_table(struct device *dev, const char *name, 61162306a36Sopenharmony_ci const char *parent_name, unsigned long flags, 61262306a36Sopenharmony_ci void __iomem *reg, u8 shift, u8 width, 61362306a36Sopenharmony_ci u8 clk_divider_flags, const struct clk_div_table *table, 61462306a36Sopenharmony_ci spinlock_t *lock) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct clk_hw *hw; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci hw = __clk_hw_register_divider(dev, NULL, name, parent_name, NULL, 61962306a36Sopenharmony_ci NULL, flags, reg, shift, width, clk_divider_flags, 62062306a36Sopenharmony_ci table, lock); 62162306a36Sopenharmony_ci if (IS_ERR(hw)) 62262306a36Sopenharmony_ci return ERR_CAST(hw); 62362306a36Sopenharmony_ci return hw->clk; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_register_divider_table); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_civoid clk_unregister_divider(struct clk *clk) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct clk_divider *div; 63062306a36Sopenharmony_ci struct clk_hw *hw; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci hw = __clk_get_hw(clk); 63362306a36Sopenharmony_ci if (!hw) 63462306a36Sopenharmony_ci return; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci div = to_clk_divider(hw); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci clk_unregister(clk); 63962306a36Sopenharmony_ci kfree(div); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_unregister_divider); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/** 64462306a36Sopenharmony_ci * clk_hw_unregister_divider - unregister a clk divider 64562306a36Sopenharmony_ci * @hw: hardware-specific clock data to unregister 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_civoid clk_hw_unregister_divider(struct clk_hw *hw) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct clk_divider *div; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci div = to_clk_divider(hw); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci clk_hw_unregister(hw); 65462306a36Sopenharmony_ci kfree(div); 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(clk_hw_unregister_divider); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic void devm_clk_hw_release_divider(struct device *dev, void *res) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci clk_hw_unregister_divider(*(struct clk_hw **)res); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistruct clk_hw *__devm_clk_hw_register_divider(struct device *dev, 66462306a36Sopenharmony_ci struct device_node *np, const char *name, 66562306a36Sopenharmony_ci const char *parent_name, const struct clk_hw *parent_hw, 66662306a36Sopenharmony_ci const struct clk_parent_data *parent_data, unsigned long flags, 66762306a36Sopenharmony_ci void __iomem *reg, u8 shift, u8 width, u8 clk_divider_flags, 66862306a36Sopenharmony_ci const struct clk_div_table *table, spinlock_t *lock) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct clk_hw **ptr, *hw; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci ptr = devres_alloc(devm_clk_hw_release_divider, sizeof(*ptr), GFP_KERNEL); 67362306a36Sopenharmony_ci if (!ptr) 67462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci hw = __clk_hw_register_divider(dev, np, name, parent_name, parent_hw, 67762306a36Sopenharmony_ci parent_data, flags, reg, shift, width, 67862306a36Sopenharmony_ci clk_divider_flags, table, lock); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (!IS_ERR(hw)) { 68162306a36Sopenharmony_ci *ptr = hw; 68262306a36Sopenharmony_ci devres_add(dev, ptr); 68362306a36Sopenharmony_ci } else { 68462306a36Sopenharmony_ci devres_free(ptr); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return hw; 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__devm_clk_hw_register_divider); 690