Blame view

drivers/clk/clk-mux.c 5.47 KB
e1bd55e5a   Stephen Boyd   clk: Tag basic cl...
1
  // SPDX-License-Identifier: GPL-2.0
9d9f78ed9   Mike Turquette   clk: basic clock ...
2
3
4
5
6
  /*
   * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
   * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
   * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
   *
9d9f78ed9   Mike Turquette   clk: basic clock ...
7
8
   * Simple multiplexer clock implementation
   */
9d9f78ed9   Mike Turquette   clk: basic clock ...
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  #include <linux/clk-provider.h>
  #include <linux/module.h>
  #include <linux/slab.h>
  #include <linux/io.h>
  #include <linux/err.h>
  
  /*
   * DOC: basic adjustable multiplexer clock that cannot gate
   *
   * Traits of this clock:
   * prepare - clk_prepare only ensures that parents are prepared
   * enable - clk_enable only ensures that parents are enabled
   * rate - rate is only affected by parent switching.  No clk_set_rate support
   * parent - parent is adjustable through clk_set_parent
   */
3a7275196   Jonas Gorski   clk: mux: add exp...
24
25
26
27
  static inline u32 clk_mux_readl(struct clk_mux *mux)
  {
  	if (mux->flags & CLK_MUX_BIG_ENDIAN)
  		return ioread32be(mux->reg);
5834fd75e   Jonas Gorski   clk: core: replac...
28
  	return readl(mux->reg);
3a7275196   Jonas Gorski   clk: mux: add exp...
29
30
31
32
33
34
35
  }
  
  static inline void clk_mux_writel(struct clk_mux *mux, u32 val)
  {
  	if (mux->flags & CLK_MUX_BIG_ENDIAN)
  		iowrite32be(val, mux->reg);
  	else
5834fd75e   Jonas Gorski   clk: core: replac...
36
  		writel(val, mux->reg);
3a7275196   Jonas Gorski   clk: mux: add exp...
37
  }
77deb66d2   Jerome Brunet   clk: mux: add hel...
38
39
  int clk_mux_val_to_index(struct clk_hw *hw, u32 *table, unsigned int flags,
  			 unsigned int val)
9d9f78ed9   Mike Turquette   clk: basic clock ...
40
  {
497295afb   Stephen Boyd   clk: Replace __cl...
41
  	int num_parents = clk_hw_get_num_parents(hw);
9d9f78ed9   Mike Turquette   clk: basic clock ...
42

77deb66d2   Jerome Brunet   clk: mux: add hel...
43
  	if (table) {
ce4f3313b   Peter De Schrijver   clk: add table lo...
44
45
46
  		int i;
  
  		for (i = 0; i < num_parents; i++)
77deb66d2   Jerome Brunet   clk: mux: add hel...
47
  			if (table[i] == val)
ce4f3313b   Peter De Schrijver   clk: add table lo...
48
49
50
  				return i;
  		return -EINVAL;
  	}
9d9f78ed9   Mike Turquette   clk: basic clock ...
51

77deb66d2   Jerome Brunet   clk: mux: add hel...
52
  	if (val && (flags & CLK_MUX_INDEX_BIT))
9d9f78ed9   Mike Turquette   clk: basic clock ...
53
  		val = ffs(val) - 1;
77deb66d2   Jerome Brunet   clk: mux: add hel...
54
  	if (val && (flags & CLK_MUX_INDEX_ONE))
9d9f78ed9   Mike Turquette   clk: basic clock ...
55
  		val--;
ce4f3313b   Peter De Schrijver   clk: add table lo...
56
  	if (val >= num_parents)
9d9f78ed9   Mike Turquette   clk: basic clock ...
57
58
59
60
  		return -EINVAL;
  
  	return val;
  }
77deb66d2   Jerome Brunet   clk: mux: add hel...
61
  EXPORT_SYMBOL_GPL(clk_mux_val_to_index);
9d9f78ed9   Mike Turquette   clk: basic clock ...
62

77deb66d2   Jerome Brunet   clk: mux: add hel...
63
  unsigned int clk_mux_index_to_val(u32 *table, unsigned int flags, u8 index)
9d9f78ed9   Mike Turquette   clk: basic clock ...
64
  {
77deb66d2   Jerome Brunet   clk: mux: add hel...
65
  	unsigned int val = index;
9d9f78ed9   Mike Turquette   clk: basic clock ...
66

77deb66d2   Jerome Brunet   clk: mux: add hel...
67
68
  	if (table) {
  		val = table[index];
3837bd277   Masahiro Yamada   clk: fix codying ...
69
  	} else {
77deb66d2   Jerome Brunet   clk: mux: add hel...
70
71
  		if (flags & CLK_MUX_INDEX_BIT)
  			val = 1 << index;
ce4f3313b   Peter De Schrijver   clk: add table lo...
72

77deb66d2   Jerome Brunet   clk: mux: add hel...
73
74
  		if (flags & CLK_MUX_INDEX_ONE)
  			val++;
ce4f3313b   Peter De Schrijver   clk: add table lo...
75
  	}
9d9f78ed9   Mike Turquette   clk: basic clock ...
76

77deb66d2   Jerome Brunet   clk: mux: add hel...
77
78
79
80
81
82
83
84
  	return val;
  }
  EXPORT_SYMBOL_GPL(clk_mux_index_to_val);
  
  static u8 clk_mux_get_parent(struct clk_hw *hw)
  {
  	struct clk_mux *mux = to_clk_mux(hw);
  	u32 val;
3a7275196   Jonas Gorski   clk: mux: add exp...
85
  	val = clk_mux_readl(mux) >> mux->shift;
77deb66d2   Jerome Brunet   clk: mux: add hel...
86
87
88
89
90
91
92
93
94
95
96
  	val &= mux->mask;
  
  	return clk_mux_val_to_index(hw, mux->table, mux->flags, val);
  }
  
  static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
  {
  	struct clk_mux *mux = to_clk_mux(hw);
  	u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
  	unsigned long flags = 0;
  	u32 reg;
9d9f78ed9   Mike Turquette   clk: basic clock ...
97
98
  	if (mux->lock)
  		spin_lock_irqsave(mux->lock, flags);
661e2180c   Stephen Boyd   clk: basic-type: ...
99
100
  	else
  		__acquire(mux->lock);
9d9f78ed9   Mike Turquette   clk: basic clock ...
101

ba492e900   Haojian Zhuang   clk: mux: add CLK...
102
  	if (mux->flags & CLK_MUX_HIWORD_MASK) {
77deb66d2   Jerome Brunet   clk: mux: add hel...
103
  		reg = mux->mask << (mux->shift + 16);
ba492e900   Haojian Zhuang   clk: mux: add CLK...
104
  	} else {
3a7275196   Jonas Gorski   clk: mux: add exp...
105
  		reg = clk_mux_readl(mux);
77deb66d2   Jerome Brunet   clk: mux: add hel...
106
  		reg &= ~(mux->mask << mux->shift);
ba492e900   Haojian Zhuang   clk: mux: add CLK...
107
  	}
77deb66d2   Jerome Brunet   clk: mux: add hel...
108
109
  	val = val << mux->shift;
  	reg |= val;
3a7275196   Jonas Gorski   clk: mux: add exp...
110
  	clk_mux_writel(mux, reg);
9d9f78ed9   Mike Turquette   clk: basic clock ...
111
112
113
  
  	if (mux->lock)
  		spin_unlock_irqrestore(mux->lock, flags);
661e2180c   Stephen Boyd   clk: basic-type: ...
114
115
  	else
  		__release(mux->lock);
9d9f78ed9   Mike Turquette   clk: basic clock ...
116
117
118
  
  	return 0;
  }
9d9f78ed9   Mike Turquette   clk: basic clock ...
119

4ad69b80e   Jerome Brunet   clk: honor CLK_MU...
120
121
122
123
124
125
126
  static int clk_mux_determine_rate(struct clk_hw *hw,
  				  struct clk_rate_request *req)
  {
  	struct clk_mux *mux = to_clk_mux(hw);
  
  	return clk_mux_determine_rate_flags(hw, req, mux->flags);
  }
822c250e1   Shawn Guo   clk: add "const" ...
127
  const struct clk_ops clk_mux_ops = {
9d9f78ed9   Mike Turquette   clk: basic clock ...
128
129
  	.get_parent = clk_mux_get_parent,
  	.set_parent = clk_mux_set_parent,
4ad69b80e   Jerome Brunet   clk: honor CLK_MU...
130
  	.determine_rate = clk_mux_determine_rate,
9d9f78ed9   Mike Turquette   clk: basic clock ...
131
132
  };
  EXPORT_SYMBOL_GPL(clk_mux_ops);
c57acd14a   Tomasz Figa   clk: mux: Add sup...
133
134
135
136
  const struct clk_ops clk_mux_ro_ops = {
  	.get_parent = clk_mux_get_parent,
  };
  EXPORT_SYMBOL_GPL(clk_mux_ro_ops);
9611b3aac   Stephen Boyd   clk: mux: Add sup...
137
138
139
140
141
142
  struct clk_hw *__clk_hw_register_mux(struct device *dev, struct device_node *np,
  		const char *name, u8 num_parents,
  		const char * const *parent_names,
  		const struct clk_hw **parent_hws,
  		const struct clk_parent_data *parent_data,
  		unsigned long flags, void __iomem *reg, u8 shift, u32 mask,
ce4f3313b   Peter De Schrijver   clk: add table lo...
143
  		u8 clk_mux_flags, u32 *table, spinlock_t *lock)
9d9f78ed9   Mike Turquette   clk: basic clock ...
144
145
  {
  	struct clk_mux *mux;
264b31719   Stephen Boyd   clk: mux: Add hw ...
146
  	struct clk_hw *hw;
cc819cf8d   Manivannan Sadhasivam   clk: Zero init cl...
147
  	struct clk_init_data init = {};
ba492e900   Haojian Zhuang   clk: mux: add CLK...
148
  	u8 width = 0;
9611b3aac   Stephen Boyd   clk: mux: Add sup...
149
  	int ret = -EINVAL;
ba492e900   Haojian Zhuang   clk: mux: add CLK...
150
151
152
153
154
155
156
157
158
  
  	if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
  		width = fls(mask) - ffs(mask) + 1;
  		if (width + shift > 16) {
  			pr_err("mux value exceeds LOWORD field
  ");
  			return ERR_PTR(-EINVAL);
  		}
  	}
9d9f78ed9   Mike Turquette   clk: basic clock ...
159

27d545915   Mike Turquette   clk: basic: impro...
160
  	/* allocate the mux */
1e28733e3   Markus Elfring   clk: clk-mux: Imp...
161
  	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
0b910402c   Markus Elfring   clk: clk-mux: Del...
162
  	if (!mux)
9d9f78ed9   Mike Turquette   clk: basic clock ...
163
  		return ERR_PTR(-ENOMEM);
9d9f78ed9   Mike Turquette   clk: basic clock ...
164

0197b3ea0   Saravana Kannan   clk: Use a separa...
165
  	init.name = name;
c57acd14a   Tomasz Figa   clk: mux: Add sup...
166
167
168
169
  	if (clk_mux_flags & CLK_MUX_READ_ONLY)
  		init.ops = &clk_mux_ro_ops;
  	else
  		init.ops = &clk_mux_ops;
90b6c5c73   Stephen Boyd   clk: Remove CLK_I...
170
  	init.flags = flags;
0197b3ea0   Saravana Kannan   clk: Use a separa...
171
  	init.parent_names = parent_names;
9611b3aac   Stephen Boyd   clk: mux: Add sup...
172
173
  	init.parent_data = parent_data;
  	init.parent_hws = parent_hws;
0197b3ea0   Saravana Kannan   clk: Use a separa...
174
  	init.num_parents = num_parents;
9d9f78ed9   Mike Turquette   clk: basic clock ...
175
176
177
  	/* struct clk_mux assignments */
  	mux->reg = reg;
  	mux->shift = shift;
ce4f3313b   Peter De Schrijver   clk: add table lo...
178
  	mux->mask = mask;
9d9f78ed9   Mike Turquette   clk: basic clock ...
179
180
  	mux->flags = clk_mux_flags;
  	mux->lock = lock;
ce4f3313b   Peter De Schrijver   clk: add table lo...
181
  	mux->table = table;
31df9db99   Mike Turquette   clk: mux: assign ...
182
  	mux->hw.init = &init;
9d9f78ed9   Mike Turquette   clk: basic clock ...
183

264b31719   Stephen Boyd   clk: mux: Add hw ...
184
  	hw = &mux->hw;
9611b3aac   Stephen Boyd   clk: mux: Add sup...
185
186
187
188
  	if (dev || !np)
  		ret = clk_hw_register(dev, hw);
  	else if (np)
  		ret = of_clk_hw_register(np, hw);
264b31719   Stephen Boyd   clk: mux: Add hw ...
189
  	if (ret) {
27d545915   Mike Turquette   clk: basic: impro...
190
  		kfree(mux);
264b31719   Stephen Boyd   clk: mux: Add hw ...
191
192
  		hw = ERR_PTR(ret);
  	}
27d545915   Mike Turquette   clk: basic: impro...
193

264b31719   Stephen Boyd   clk: mux: Add hw ...
194
195
  	return hw;
  }
9611b3aac   Stephen Boyd   clk: mux: Add sup...
196
  EXPORT_SYMBOL_GPL(__clk_hw_register_mux);
264b31719   Stephen Boyd   clk: mux: Add hw ...
197
198
199
  
  struct clk *clk_register_mux_table(struct device *dev, const char *name,
  		const char * const *parent_names, u8 num_parents,
9611b3aac   Stephen Boyd   clk: mux: Add sup...
200
  		unsigned long flags, void __iomem *reg, u8 shift, u32 mask,
264b31719   Stephen Boyd   clk: mux: Add hw ...
201
202
203
  		u8 clk_mux_flags, u32 *table, spinlock_t *lock)
  {
  	struct clk_hw *hw;
9611b3aac   Stephen Boyd   clk: mux: Add sup...
204
205
206
  	hw = clk_hw_register_mux_table(dev, name, parent_names,
  				       num_parents, flags, reg, shift, mask,
  				       clk_mux_flags, table, lock);
264b31719   Stephen Boyd   clk: mux: Add hw ...
207
208
209
  	if (IS_ERR(hw))
  		return ERR_CAST(hw);
  	return hw->clk;
9d9f78ed9   Mike Turquette   clk: basic clock ...
210
  }
5cfe10bb0   Mike Turquette   clk: export fixed...
211
  EXPORT_SYMBOL_GPL(clk_register_mux_table);
ce4f3313b   Peter De Schrijver   clk: add table lo...
212

4e3c021fb   Krzysztof Kozlowski   clk: Add clk_unre...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  void clk_unregister_mux(struct clk *clk)
  {
  	struct clk_mux *mux;
  	struct clk_hw *hw;
  
  	hw = __clk_get_hw(clk);
  	if (!hw)
  		return;
  
  	mux = to_clk_mux(hw);
  
  	clk_unregister(clk);
  	kfree(mux);
  }
  EXPORT_SYMBOL_GPL(clk_unregister_mux);
264b31719   Stephen Boyd   clk: mux: Add hw ...
228
229
230
231
232
233
234
235
236
237
238
  
  void clk_hw_unregister_mux(struct clk_hw *hw)
  {
  	struct clk_mux *mux;
  
  	mux = to_clk_mux(hw);
  
  	clk_hw_unregister(hw);
  	kfree(mux);
  }
  EXPORT_SYMBOL_GPL(clk_hw_unregister_mux);