1956060a5SElaine Zhang // SPDX-License-Identifier: GPL-2.0 2956060a5SElaine Zhang /* 3956060a5SElaine Zhang * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd 4956060a5SElaine Zhang */ 5956060a5SElaine Zhang 6956060a5SElaine Zhang #include <linux/clk-provider.h> 762e59c4eSStephen Boyd #include <linux/io.h> 862e59c4eSStephen Boyd #include <linux/slab.h> 9956060a5SElaine Zhang #include "clk.h" 10956060a5SElaine Zhang 11956060a5SElaine Zhang #define div_mask(width) ((1 << (width)) - 1) 12956060a5SElaine Zhang 13956060a5SElaine Zhang static bool _is_best_half_div(unsigned long rate, unsigned long now, 14956060a5SElaine Zhang unsigned long best, unsigned long flags) 15956060a5SElaine Zhang { 16956060a5SElaine Zhang if (flags & CLK_DIVIDER_ROUND_CLOSEST) 17956060a5SElaine Zhang return abs(rate - now) < abs(rate - best); 18956060a5SElaine Zhang 19956060a5SElaine Zhang return now <= rate && now > best; 20956060a5SElaine Zhang } 21956060a5SElaine Zhang 22956060a5SElaine Zhang static unsigned long clk_half_divider_recalc_rate(struct clk_hw *hw, 23956060a5SElaine Zhang unsigned long parent_rate) 24956060a5SElaine Zhang { 25956060a5SElaine Zhang struct clk_divider *divider = to_clk_divider(hw); 26956060a5SElaine Zhang unsigned int val; 27956060a5SElaine Zhang 285834fd75SJonas Gorski val = readl(divider->reg) >> divider->shift; 29956060a5SElaine Zhang val &= div_mask(divider->width); 30956060a5SElaine Zhang val = val * 2 + 3; 31956060a5SElaine Zhang 32956060a5SElaine Zhang return DIV_ROUND_UP_ULL(((u64)parent_rate * 2), val); 33956060a5SElaine Zhang } 34956060a5SElaine Zhang 35956060a5SElaine Zhang static int clk_half_divider_bestdiv(struct clk_hw *hw, unsigned long rate, 36956060a5SElaine Zhang unsigned long *best_parent_rate, u8 width, 37956060a5SElaine Zhang unsigned long flags) 38956060a5SElaine Zhang { 39956060a5SElaine Zhang unsigned int i, bestdiv = 0; 40956060a5SElaine Zhang unsigned long parent_rate, best = 0, now, maxdiv; 41956060a5SElaine Zhang unsigned long parent_rate_saved = *best_parent_rate; 42956060a5SElaine Zhang 43956060a5SElaine Zhang if (!rate) 44956060a5SElaine Zhang rate = 1; 45956060a5SElaine Zhang 46956060a5SElaine Zhang maxdiv = div_mask(width); 47956060a5SElaine Zhang 48956060a5SElaine Zhang if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { 49956060a5SElaine Zhang parent_rate = *best_parent_rate; 50956060a5SElaine Zhang bestdiv = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate); 51956060a5SElaine Zhang if (bestdiv < 3) 52956060a5SElaine Zhang bestdiv = 0; 53956060a5SElaine Zhang else 54956060a5SElaine Zhang bestdiv = (bestdiv - 3) / 2; 55956060a5SElaine Zhang bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv; 56956060a5SElaine Zhang return bestdiv; 57956060a5SElaine Zhang } 58956060a5SElaine Zhang 59956060a5SElaine Zhang /* 60956060a5SElaine Zhang * The maximum divider we can use without overflowing 61956060a5SElaine Zhang * unsigned long in rate * i below 62956060a5SElaine Zhang */ 63956060a5SElaine Zhang maxdiv = min(ULONG_MAX / rate, maxdiv); 64956060a5SElaine Zhang 65956060a5SElaine Zhang for (i = 0; i <= maxdiv; i++) { 66956060a5SElaine Zhang if (((u64)rate * (i * 2 + 3)) == ((u64)parent_rate_saved * 2)) { 67956060a5SElaine Zhang /* 68956060a5SElaine Zhang * It's the most ideal case if the requested rate can be 69956060a5SElaine Zhang * divided from parent clock without needing to change 70956060a5SElaine Zhang * parent rate, so return the divider immediately. 71956060a5SElaine Zhang */ 72956060a5SElaine Zhang *best_parent_rate = parent_rate_saved; 73956060a5SElaine Zhang return i; 74956060a5SElaine Zhang } 75956060a5SElaine Zhang parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 76956060a5SElaine Zhang ((u64)rate * (i * 2 + 3)) / 2); 77956060a5SElaine Zhang now = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), 78956060a5SElaine Zhang (i * 2 + 3)); 79956060a5SElaine Zhang 80956060a5SElaine Zhang if (_is_best_half_div(rate, now, best, flags)) { 81956060a5SElaine Zhang bestdiv = i; 82956060a5SElaine Zhang best = now; 83956060a5SElaine Zhang *best_parent_rate = parent_rate; 84956060a5SElaine Zhang } 85956060a5SElaine Zhang } 86956060a5SElaine Zhang 87956060a5SElaine Zhang if (!bestdiv) { 88956060a5SElaine Zhang bestdiv = div_mask(width); 89956060a5SElaine Zhang *best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1); 90956060a5SElaine Zhang } 91956060a5SElaine Zhang 92956060a5SElaine Zhang return bestdiv; 93956060a5SElaine Zhang } 94956060a5SElaine Zhang 95956060a5SElaine Zhang static long clk_half_divider_round_rate(struct clk_hw *hw, unsigned long rate, 96956060a5SElaine Zhang unsigned long *prate) 97956060a5SElaine Zhang { 98956060a5SElaine Zhang struct clk_divider *divider = to_clk_divider(hw); 99956060a5SElaine Zhang int div; 100956060a5SElaine Zhang 101956060a5SElaine Zhang div = clk_half_divider_bestdiv(hw, rate, prate, 102956060a5SElaine Zhang divider->width, 103956060a5SElaine Zhang divider->flags); 104956060a5SElaine Zhang 105956060a5SElaine Zhang return DIV_ROUND_UP_ULL(((u64)*prate * 2), div * 2 + 3); 106956060a5SElaine Zhang } 107956060a5SElaine Zhang 108956060a5SElaine Zhang static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate, 109956060a5SElaine Zhang unsigned long parent_rate) 110956060a5SElaine Zhang { 111956060a5SElaine Zhang struct clk_divider *divider = to_clk_divider(hw); 112956060a5SElaine Zhang unsigned int value; 113956060a5SElaine Zhang unsigned long flags = 0; 114956060a5SElaine Zhang u32 val; 115956060a5SElaine Zhang 116956060a5SElaine Zhang value = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate); 117956060a5SElaine Zhang value = (value - 3) / 2; 118956060a5SElaine Zhang value = min_t(unsigned int, value, div_mask(divider->width)); 119956060a5SElaine Zhang 120956060a5SElaine Zhang if (divider->lock) 121956060a5SElaine Zhang spin_lock_irqsave(divider->lock, flags); 122956060a5SElaine Zhang else 123956060a5SElaine Zhang __acquire(divider->lock); 124956060a5SElaine Zhang 125956060a5SElaine Zhang if (divider->flags & CLK_DIVIDER_HIWORD_MASK) { 126956060a5SElaine Zhang val = div_mask(divider->width) << (divider->shift + 16); 127956060a5SElaine Zhang } else { 1285834fd75SJonas Gorski val = readl(divider->reg); 129956060a5SElaine Zhang val &= ~(div_mask(divider->width) << divider->shift); 130956060a5SElaine Zhang } 131956060a5SElaine Zhang val |= value << divider->shift; 1325834fd75SJonas Gorski writel(val, divider->reg); 133956060a5SElaine Zhang 134956060a5SElaine Zhang if (divider->lock) 135956060a5SElaine Zhang spin_unlock_irqrestore(divider->lock, flags); 136956060a5SElaine Zhang else 137956060a5SElaine Zhang __release(divider->lock); 138956060a5SElaine Zhang 139956060a5SElaine Zhang return 0; 140956060a5SElaine Zhang } 141956060a5SElaine Zhang 142bbbbd246SBen Dooks (Codethink) static const struct clk_ops clk_half_divider_ops = { 143956060a5SElaine Zhang .recalc_rate = clk_half_divider_recalc_rate, 144956060a5SElaine Zhang .round_rate = clk_half_divider_round_rate, 145956060a5SElaine Zhang .set_rate = clk_half_divider_set_rate, 146956060a5SElaine Zhang }; 147956060a5SElaine Zhang 148956060a5SElaine Zhang /** 149956060a5SElaine Zhang * Register a clock branch. 150956060a5SElaine Zhang * Most clock branches have a form like 151956060a5SElaine Zhang * 152956060a5SElaine Zhang * src1 --|--\ 153956060a5SElaine Zhang * |M |--[GATE]-[DIV]- 154956060a5SElaine Zhang * src2 --|--/ 155956060a5SElaine Zhang * 156956060a5SElaine Zhang * sometimes without one of those components. 157956060a5SElaine Zhang */ 158956060a5SElaine Zhang struct clk *rockchip_clk_register_halfdiv(const char *name, 159956060a5SElaine Zhang const char *const *parent_names, 160956060a5SElaine Zhang u8 num_parents, void __iomem *base, 161956060a5SElaine Zhang int muxdiv_offset, u8 mux_shift, 162956060a5SElaine Zhang u8 mux_width, u8 mux_flags, 163956060a5SElaine Zhang u8 div_shift, u8 div_width, 164956060a5SElaine Zhang u8 div_flags, int gate_offset, 165956060a5SElaine Zhang u8 gate_shift, u8 gate_flags, 166956060a5SElaine Zhang unsigned long flags, 167956060a5SElaine Zhang spinlock_t *lock) 168956060a5SElaine Zhang { 169b608f11dSStephen Boyd struct clk_hw *hw = ERR_PTR(-ENOMEM); 170956060a5SElaine Zhang struct clk_mux *mux = NULL; 171956060a5SElaine Zhang struct clk_gate *gate = NULL; 172956060a5SElaine Zhang struct clk_divider *div = NULL; 173956060a5SElaine Zhang const struct clk_ops *mux_ops = NULL, *div_ops = NULL, 174956060a5SElaine Zhang *gate_ops = NULL; 175956060a5SElaine Zhang 176956060a5SElaine Zhang if (num_parents > 1) { 177956060a5SElaine Zhang mux = kzalloc(sizeof(*mux), GFP_KERNEL); 178956060a5SElaine Zhang if (!mux) 179956060a5SElaine Zhang return ERR_PTR(-ENOMEM); 180956060a5SElaine Zhang 181956060a5SElaine Zhang mux->reg = base + muxdiv_offset; 182956060a5SElaine Zhang mux->shift = mux_shift; 183956060a5SElaine Zhang mux->mask = BIT(mux_width) - 1; 184956060a5SElaine Zhang mux->flags = mux_flags; 185956060a5SElaine Zhang mux->lock = lock; 186956060a5SElaine Zhang mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops 187956060a5SElaine Zhang : &clk_mux_ops; 188956060a5SElaine Zhang } 189956060a5SElaine Zhang 190956060a5SElaine Zhang if (gate_offset >= 0) { 191956060a5SElaine Zhang gate = kzalloc(sizeof(*gate), GFP_KERNEL); 192956060a5SElaine Zhang if (!gate) 193956060a5SElaine Zhang goto err_gate; 194956060a5SElaine Zhang 195956060a5SElaine Zhang gate->flags = gate_flags; 196956060a5SElaine Zhang gate->reg = base + gate_offset; 197956060a5SElaine Zhang gate->bit_idx = gate_shift; 198956060a5SElaine Zhang gate->lock = lock; 199956060a5SElaine Zhang gate_ops = &clk_gate_ops; 200956060a5SElaine Zhang } 201956060a5SElaine Zhang 202956060a5SElaine Zhang if (div_width > 0) { 203956060a5SElaine Zhang div = kzalloc(sizeof(*div), GFP_KERNEL); 204956060a5SElaine Zhang if (!div) 205956060a5SElaine Zhang goto err_div; 206956060a5SElaine Zhang 207956060a5SElaine Zhang div->flags = div_flags; 208956060a5SElaine Zhang div->reg = base + muxdiv_offset; 209956060a5SElaine Zhang div->shift = div_shift; 210956060a5SElaine Zhang div->width = div_width; 211956060a5SElaine Zhang div->lock = lock; 212956060a5SElaine Zhang div_ops = &clk_half_divider_ops; 213956060a5SElaine Zhang } 214956060a5SElaine Zhang 21563207c37SElaine Zhang hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 216956060a5SElaine Zhang mux ? &mux->hw : NULL, mux_ops, 217956060a5SElaine Zhang div ? &div->hw : NULL, div_ops, 218956060a5SElaine Zhang gate ? &gate->hw : NULL, gate_ops, 219956060a5SElaine Zhang flags); 22063207c37SElaine Zhang if (IS_ERR(hw)) 22163207c37SElaine Zhang goto err_div; 222956060a5SElaine Zhang 22363207c37SElaine Zhang return hw->clk; 224956060a5SElaine Zhang err_div: 225956060a5SElaine Zhang kfree(gate); 226956060a5SElaine Zhang err_gate: 227956060a5SElaine Zhang kfree(mux); 22863207c37SElaine Zhang return ERR_CAST(hw); 229956060a5SElaine Zhang } 230