Blame view

drivers/clk/clk-multiplier.c 3.84 KB
e1bd55e5a   Stephen Boyd   clk: Tag basic cl...
1
  // SPDX-License-Identifier: GPL-2.0
f2e0a5327   Maxime Ripard   clk: Add a basic ...
2
3
  /*
   * Copyright (C) 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
f2e0a5327   Maxime Ripard   clk: Add a basic ...
4
5
6
7
8
9
   */
  
  #include <linux/bitops.h>
  #include <linux/clk-provider.h>
  #include <linux/err.h>
  #include <linux/export.h>
62e59c4e6   Stephen Boyd   clk: Remove io.h ...
10
  #include <linux/io.h>
f2e0a5327   Maxime Ripard   clk: Add a basic ...
11
12
13
  #include <linux/kernel.h>
  #include <linux/of.h>
  #include <linux/slab.h>
9427b71a8   Jonas Gorski   clk: multiplier: ...
14
15
16
17
  static inline u32 clk_mult_readl(struct clk_multiplier *mult)
  {
  	if (mult->flags & CLK_MULTIPLIER_BIG_ENDIAN)
  		return ioread32be(mult->reg);
5834fd75e   Jonas Gorski   clk: core: replac...
18
  	return readl(mult->reg);
9427b71a8   Jonas Gorski   clk: multiplier: ...
19
20
21
22
23
24
25
  }
  
  static inline void clk_mult_writel(struct clk_multiplier *mult, u32 val)
  {
  	if (mult->flags & CLK_MULTIPLIER_BIG_ENDIAN)
  		iowrite32be(val, mult->reg);
  	else
5834fd75e   Jonas Gorski   clk: core: replac...
26
  		writel(val, mult->reg);
9427b71a8   Jonas Gorski   clk: multiplier: ...
27
  }
f2e0a5327   Maxime Ripard   clk: Add a basic ...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
  static unsigned long __get_mult(struct clk_multiplier *mult,
  				unsigned long rate,
  				unsigned long parent_rate)
  {
  	if (mult->flags & CLK_MULTIPLIER_ROUND_CLOSEST)
  		return DIV_ROUND_CLOSEST(rate, parent_rate);
  
  	return rate / parent_rate;
  }
  
  static unsigned long clk_multiplier_recalc_rate(struct clk_hw *hw,
  						unsigned long parent_rate)
  {
  	struct clk_multiplier *mult = to_clk_multiplier(hw);
  	unsigned long val;
9427b71a8   Jonas Gorski   clk: multiplier: ...
43
  	val = clk_mult_readl(mult) >> mult->shift;
f2e0a5327   Maxime Ripard   clk: Add a basic ...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  	val &= GENMASK(mult->width - 1, 0);
  
  	if (!val && mult->flags & CLK_MULTIPLIER_ZERO_BYPASS)
  		val = 1;
  
  	return parent_rate * val;
  }
  
  static bool __is_best_rate(unsigned long rate, unsigned long new,
  			   unsigned long best, unsigned long flags)
  {
  	if (flags & CLK_MULTIPLIER_ROUND_CLOSEST)
  		return abs(rate - new) < abs(rate - best);
  
  	return new >= rate && new < best;
  }
  
  static unsigned long __bestmult(struct clk_hw *hw, unsigned long rate,
  				unsigned long *best_parent_rate,
  				u8 width, unsigned long flags)
  {
25f77a3aa   Maxime Ripard   clk: multiplier: ...
65
  	struct clk_multiplier *mult = to_clk_multiplier(hw);
f2e0a5327   Maxime Ripard   clk: Add a basic ...
66
67
68
  	unsigned long orig_parent_rate = *best_parent_rate;
  	unsigned long parent_rate, current_rate, best_rate = ~0;
  	unsigned int i, bestmult = 0;
25f77a3aa   Maxime Ripard   clk: multiplier: ...
69
70
71
72
73
74
75
76
77
  	unsigned int maxmult = (1 << width) - 1;
  
  	if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
  		bestmult = rate / orig_parent_rate;
  
  		/* Make sure we don't end up with a 0 multiplier */
  		if ((bestmult == 0) &&
  		    !(mult->flags & CLK_MULTIPLIER_ZERO_BYPASS))
  			bestmult = 1;
f2e0a5327   Maxime Ripard   clk: Add a basic ...
78

25f77a3aa   Maxime Ripard   clk: multiplier: ...
79
80
81
82
83
84
  		/* Make sure we don't overflow the multiplier */
  		if (bestmult > maxmult)
  			bestmult = maxmult;
  
  		return bestmult;
  	}
f2e0a5327   Maxime Ripard   clk: Add a basic ...
85

25f77a3aa   Maxime Ripard   clk: multiplier: ...
86
  	for (i = 1; i < maxmult; i++) {
f2e0a5327   Maxime Ripard   clk: Add a basic ...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
  		if (rate == orig_parent_rate * i) {
  			/*
  			 * This is the best case for us if we have a
  			 * perfect match without changing the parent
  			 * rate.
  			 */
  			*best_parent_rate = orig_parent_rate;
  			return i;
  		}
  
  		parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
  						rate / i);
  		current_rate = parent_rate * i;
  
  		if (__is_best_rate(rate, current_rate, best_rate, flags)) {
  			bestmult = i;
  			best_rate = current_rate;
  			*best_parent_rate = parent_rate;
  		}
  	}
  
  	return bestmult;
  }
  
  static long clk_multiplier_round_rate(struct clk_hw *hw, unsigned long rate,
  				  unsigned long *parent_rate)
  {
  	struct clk_multiplier *mult = to_clk_multiplier(hw);
  	unsigned long factor = __bestmult(hw, rate, parent_rate,
  					  mult->width, mult->flags);
  
  	return *parent_rate * factor;
  }
  
  static int clk_multiplier_set_rate(struct clk_hw *hw, unsigned long rate,
  			       unsigned long parent_rate)
  {
  	struct clk_multiplier *mult = to_clk_multiplier(hw);
  	unsigned long factor = __get_mult(mult, rate, parent_rate);
  	unsigned long flags = 0;
  	unsigned long val;
  
  	if (mult->lock)
  		spin_lock_irqsave(mult->lock, flags);
  	else
  		__acquire(mult->lock);
9427b71a8   Jonas Gorski   clk: multiplier: ...
133
  	val = clk_mult_readl(mult);
f2e0a5327   Maxime Ripard   clk: Add a basic ...
134
135
  	val &= ~GENMASK(mult->width + mult->shift - 1, mult->shift);
  	val |= factor << mult->shift;
9427b71a8   Jonas Gorski   clk: multiplier: ...
136
  	clk_mult_writel(mult, val);
f2e0a5327   Maxime Ripard   clk: Add a basic ...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  
  	if (mult->lock)
  		spin_unlock_irqrestore(mult->lock, flags);
  	else
  		__release(mult->lock);
  
  	return 0;
  }
  
  const struct clk_ops clk_multiplier_ops = {
  	.recalc_rate	= clk_multiplier_recalc_rate,
  	.round_rate	= clk_multiplier_round_rate,
  	.set_rate	= clk_multiplier_set_rate,
  };
  EXPORT_SYMBOL_GPL(clk_multiplier_ops);