1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2012 Freescale Semiconductor, Inc. 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/err.h> 8 #include <linux/io.h> 9 #include <linux/slab.h> 10 #include "clk.h" 11 12 /** 13 * struct clk_frac - mxs fractional divider clock 14 * @hw: clk_hw for the fractional divider clock 15 * @reg: register address 16 * @shift: the divider bit shift 17 * @width: the divider bit width 18 * @busy: busy bit shift 19 * 20 * The clock is an adjustable fractional divider with a busy bit to wait 21 * when the divider is adjusted. 22 */ 23 struct clk_frac { 24 struct clk_hw hw; 25 void __iomem *reg; 26 u8 shift; 27 u8 width; 28 u8 busy; 29 }; 30 31 #define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) 32 33 static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, 34 unsigned long parent_rate) 35 { 36 struct clk_frac *frac = to_clk_frac(hw); 37 u32 div; 38 u64 tmp_rate; 39 40 div = readl_relaxed(frac->reg) >> frac->shift; 41 div &= (1 << frac->width) - 1; 42 43 tmp_rate = (u64)parent_rate * div; 44 return tmp_rate >> frac->width; 45 } 46 47 static int clk_frac_determine_rate(struct clk_hw *hw, 48 struct clk_rate_request *req) 49 { 50 struct clk_frac *frac = to_clk_frac(hw); 51 unsigned long parent_rate = req->best_parent_rate; 52 u32 div; 53 u64 tmp, tmp_rate, result; 54 55 if (req->rate > parent_rate) 56 return -EINVAL; 57 58 tmp = req->rate; 59 tmp <<= frac->width; 60 do_div(tmp, parent_rate); 61 div = tmp; 62 63 if (!div) 64 return -EINVAL; 65 66 tmp_rate = (u64)parent_rate * div; 67 result = tmp_rate >> frac->width; 68 if ((result << frac->width) < tmp_rate) 69 result += 1; 70 req->rate = result; 71 72 return 0; 73 } 74 75 static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate, 76 unsigned long parent_rate) 77 { 78 struct clk_frac *frac = to_clk_frac(hw); 79 unsigned long flags; 80 u32 div, val; 81 u64 tmp; 82 83 if (rate > parent_rate) 84 return -EINVAL; 85 86 tmp = rate; 87 tmp <<= frac->width; 88 do_div(tmp, parent_rate); 89 div = tmp; 90 91 if (!div) 92 return -EINVAL; 93 94 spin_lock_irqsave(&mxs_lock, flags); 95 96 val = readl_relaxed(frac->reg); 97 val &= ~(((1 << frac->width) - 1) << frac->shift); 98 val |= div << frac->shift; 99 writel_relaxed(val, frac->reg); 100 101 spin_unlock_irqrestore(&mxs_lock, flags); 102 103 return mxs_clk_wait(frac->reg, frac->busy); 104 } 105 106 static const struct clk_ops clk_frac_ops = { 107 .recalc_rate = clk_frac_recalc_rate, 108 .determine_rate = clk_frac_determine_rate, 109 .set_rate = clk_frac_set_rate, 110 }; 111 112 struct clk *mxs_clk_frac(const char *name, const char *parent_name, 113 void __iomem *reg, u8 shift, u8 width, u8 busy) 114 { 115 struct clk_frac *frac; 116 struct clk *clk; 117 struct clk_init_data init; 118 119 frac = kzalloc(sizeof(*frac), GFP_KERNEL); 120 if (!frac) 121 return ERR_PTR(-ENOMEM); 122 123 init.name = name; 124 init.ops = &clk_frac_ops; 125 init.flags = CLK_SET_RATE_PARENT; 126 init.parent_names = (parent_name ? &parent_name: NULL); 127 init.num_parents = (parent_name ? 1 : 0); 128 129 frac->reg = reg; 130 frac->shift = shift; 131 frac->width = width; 132 frac->busy = busy; 133 frac->hw.init = &init; 134 135 clk = clk_register(NULL, &frac->hw); 136 if (IS_ERR(clk)) 137 kfree(frac); 138 139 return clk; 140 } 141