Blame view

drivers/clk/clk-gate.c 4.82 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
  /*
   * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
   * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
   *
9d9f78ed9   Mike Turquette   clk: basic clock ...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
   * Gated clock implementation
   */
  
  #include <linux/clk-provider.h>
  #include <linux/module.h>
  #include <linux/slab.h>
  #include <linux/io.h>
  #include <linux/err.h>
  #include <linux/string.h>
  
  /**
   * DOC: basic gatable clock which can gate and ungate it's ouput
   *
   * Traits of this clock:
   * prepare - clk_(un)prepare only ensures parent is (un)prepared
   * enable - clk_enable and clk_disable are functional & control gating
   * rate - inherits rate from parent.  No clk_set_rate support
   * parent - fixed parent.  No clk_set_parent support
   */
d1c8a501e   Jonas Gorski   clk: gate: add ex...
25
26
27
28
  static inline u32 clk_gate_readl(struct clk_gate *gate)
  {
  	if (gate->flags & CLK_GATE_BIG_ENDIAN)
  		return ioread32be(gate->reg);
5834fd75e   Jonas Gorski   clk: core: replac...
29
  	return readl(gate->reg);
d1c8a501e   Jonas Gorski   clk: gate: add ex...
30
31
32
33
34
35
36
  }
  
  static inline void clk_gate_writel(struct clk_gate *gate, u32 val)
  {
  	if (gate->flags & CLK_GATE_BIG_ENDIAN)
  		iowrite32be(val, gate->reg);
  	else
5834fd75e   Jonas Gorski   clk: core: replac...
37
  		writel(val, gate->reg);
d1c8a501e   Jonas Gorski   clk: gate: add ex...
38
  }
fbc42aab5   Viresh Kumar   clk: clk-gate: Cr...
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  /*
   * It works on following logic:
   *
   * For enabling clock, enable = 1
   *	set2dis = 1	-> clear bit	-> set = 0
   *	set2dis = 0	-> set bit	-> set = 1
   *
   * For disabling clock, enable = 0
   *	set2dis = 1	-> set bit	-> set = 1
   *	set2dis = 0	-> clear bit	-> set = 0
   *
   * So, result is always: enable xor set2dis.
   */
  static void clk_gate_endisable(struct clk_hw *hw, int enable)
9d9f78ed9   Mike Turquette   clk: basic clock ...
53
  {
fbc42aab5   Viresh Kumar   clk: clk-gate: Cr...
54
55
  	struct clk_gate *gate = to_clk_gate(hw);
  	int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
3f649ab72   Kees Cook   treewide: Remove ...
56
  	unsigned long flags;
fbc42aab5   Viresh Kumar   clk: clk-gate: Cr...
57
58
59
  	u32 reg;
  
  	set ^= enable;
9d9f78ed9   Mike Turquette   clk: basic clock ...
60
61
62
  
  	if (gate->lock)
  		spin_lock_irqsave(gate->lock, flags);
661e2180c   Stephen Boyd   clk: basic-type: ...
63
64
  	else
  		__acquire(gate->lock);
9d9f78ed9   Mike Turquette   clk: basic clock ...
65

045779942   Haojian Zhuang   clk: gate: add CL...
66
67
68
69
70
  	if (gate->flags & CLK_GATE_HIWORD_MASK) {
  		reg = BIT(gate->bit_idx + 16);
  		if (set)
  			reg |= BIT(gate->bit_idx);
  	} else {
d1c8a501e   Jonas Gorski   clk: gate: add ex...
71
  		reg = clk_gate_readl(gate);
045779942   Haojian Zhuang   clk: gate: add CL...
72
73
74
75
76
77
  
  		if (set)
  			reg |= BIT(gate->bit_idx);
  		else
  			reg &= ~BIT(gate->bit_idx);
  	}
9d9f78ed9   Mike Turquette   clk: basic clock ...
78

d1c8a501e   Jonas Gorski   clk: gate: add ex...
79
  	clk_gate_writel(gate, reg);
9d9f78ed9   Mike Turquette   clk: basic clock ...
80
81
82
  
  	if (gate->lock)
  		spin_unlock_irqrestore(gate->lock, flags);
661e2180c   Stephen Boyd   clk: basic-type: ...
83
84
  	else
  		__release(gate->lock);
9d9f78ed9   Mike Turquette   clk: basic clock ...
85
86
87
88
  }
  
  static int clk_gate_enable(struct clk_hw *hw)
  {
fbc42aab5   Viresh Kumar   clk: clk-gate: Cr...
89
  	clk_gate_endisable(hw, 1);
9d9f78ed9   Mike Turquette   clk: basic clock ...
90
91
92
  
  	return 0;
  }
9d9f78ed9   Mike Turquette   clk: basic clock ...
93
94
95
  
  static void clk_gate_disable(struct clk_hw *hw)
  {
fbc42aab5   Viresh Kumar   clk: clk-gate: Cr...
96
  	clk_gate_endisable(hw, 0);
9d9f78ed9   Mike Turquette   clk: basic clock ...
97
  }
9d9f78ed9   Mike Turquette   clk: basic clock ...
98

0a9c869d5   Gabriel Fernandez   clk: gate: expose...
99
  int clk_gate_is_enabled(struct clk_hw *hw)
9d9f78ed9   Mike Turquette   clk: basic clock ...
100
101
102
  {
  	u32 reg;
  	struct clk_gate *gate = to_clk_gate(hw);
d1c8a501e   Jonas Gorski   clk: gate: add ex...
103
  	reg = clk_gate_readl(gate);
9d9f78ed9   Mike Turquette   clk: basic clock ...
104
105
106
107
108
109
110
111
112
  
  	/* if a set bit disables this clk, flip it before masking */
  	if (gate->flags & CLK_GATE_SET_TO_DISABLE)
  		reg ^= BIT(gate->bit_idx);
  
  	reg &= BIT(gate->bit_idx);
  
  	return reg ? 1 : 0;
  }
0a9c869d5   Gabriel Fernandez   clk: gate: expose...
113
  EXPORT_SYMBOL_GPL(clk_gate_is_enabled);
9d9f78ed9   Mike Turquette   clk: basic clock ...
114

822c250e1   Shawn Guo   clk: add "const" ...
115
  const struct clk_ops clk_gate_ops = {
9d9f78ed9   Mike Turquette   clk: basic clock ...
116
117
118
119
120
  	.enable = clk_gate_enable,
  	.disable = clk_gate_disable,
  	.is_enabled = clk_gate_is_enabled,
  };
  EXPORT_SYMBOL_GPL(clk_gate_ops);
194efb6e2   Stephen Boyd   clk: gate: Add su...
121
122
123
124
125
  struct clk_hw *__clk_hw_register_gate(struct device *dev,
  		struct device_node *np, const char *name,
  		const char *parent_name, const struct clk_hw *parent_hw,
  		const struct clk_parent_data *parent_data,
  		unsigned long flags,
9d9f78ed9   Mike Turquette   clk: basic clock ...
126
127
128
129
  		void __iomem *reg, u8 bit_idx,
  		u8 clk_gate_flags, spinlock_t *lock)
  {
  	struct clk_gate *gate;
e270d8cb1   Stephen Boyd   clk: gate: Add hw...
130
  	struct clk_hw *hw;
cc819cf8d   Manivannan Sadhasivam   clk: Zero init cl...
131
  	struct clk_init_data init = {};
194efb6e2   Stephen Boyd   clk: gate: Add su...
132
  	int ret = -EINVAL;
9d9f78ed9   Mike Turquette   clk: basic clock ...
133

045779942   Haojian Zhuang   clk: gate: add CL...
134
  	if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
2e9dcdae4   Sergei Shtylyov   clk-gate: fix bit...
135
  		if (bit_idx > 15) {
045779942   Haojian Zhuang   clk: gate: add CL...
136
137
138
139
140
  			pr_err("gate bit exceeds LOWORD field
  ");
  			return ERR_PTR(-EINVAL);
  		}
  	}
27d545915   Mike Turquette   clk: basic: impro...
141
  	/* allocate the gate */
d122db7e8   Stephen Boyd   clk: basic-types:...
142
143
  	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
  	if (!gate)
27d545915   Mike Turquette   clk: basic: impro...
144
  		return ERR_PTR(-ENOMEM);
9d9f78ed9   Mike Turquette   clk: basic clock ...
145

0197b3ea0   Saravana Kannan   clk: Use a separa...
146
147
  	init.name = name;
  	init.ops = &clk_gate_ops;
90b6c5c73   Stephen Boyd   clk: Remove CLK_I...
148
  	init.flags = flags;
295face99   Uwe Kleine-König   clk: gate: fix co...
149
  	init.parent_names = parent_name ? &parent_name : NULL;
194efb6e2   Stephen Boyd   clk: gate: Add su...
150
151
152
153
154
155
  	init.parent_hws = parent_hw ? &parent_hw : NULL;
  	init.parent_data = parent_data;
  	if (parent_name || parent_hw || parent_data)
  		init.num_parents = 1;
  	else
  		init.num_parents = 0;
0197b3ea0   Saravana Kannan   clk: Use a separa...
156

9d9f78ed9   Mike Turquette   clk: basic clock ...
157
158
159
160
161
  	/* struct clk_gate assignments */
  	gate->reg = reg;
  	gate->bit_idx = bit_idx;
  	gate->flags = clk_gate_flags;
  	gate->lock = lock;
0197b3ea0   Saravana Kannan   clk: Use a separa...
162
  	gate->hw.init = &init;
9d9f78ed9   Mike Turquette   clk: basic clock ...
163

e270d8cb1   Stephen Boyd   clk: gate: Add hw...
164
  	hw = &gate->hw;
194efb6e2   Stephen Boyd   clk: gate: Add su...
165
166
167
168
  	if (dev || !np)
  		ret = clk_hw_register(dev, hw);
  	else if (np)
  		ret = of_clk_hw_register(np, hw);
e270d8cb1   Stephen Boyd   clk: gate: Add hw...
169
  	if (ret) {
27d545915   Mike Turquette   clk: basic: impro...
170
  		kfree(gate);
e270d8cb1   Stephen Boyd   clk: gate: Add hw...
171
172
  		hw = ERR_PTR(ret);
  	}
27d545915   Mike Turquette   clk: basic: impro...
173

e270d8cb1   Stephen Boyd   clk: gate: Add hw...
174
  	return hw;
194efb6e2   Stephen Boyd   clk: gate: Add su...
175

e270d8cb1   Stephen Boyd   clk: gate: Add hw...
176
  }
194efb6e2   Stephen Boyd   clk: gate: Add su...
177
  EXPORT_SYMBOL_GPL(__clk_hw_register_gate);
e270d8cb1   Stephen Boyd   clk: gate: Add hw...
178
179
180
181
182
183
184
185
186
187
188
189
190
  
  struct clk *clk_register_gate(struct device *dev, const char *name,
  		const char *parent_name, unsigned long flags,
  		void __iomem *reg, u8 bit_idx,
  		u8 clk_gate_flags, spinlock_t *lock)
  {
  	struct clk_hw *hw;
  
  	hw = clk_hw_register_gate(dev, name, parent_name, flags, reg,
  				  bit_idx, clk_gate_flags, lock);
  	if (IS_ERR(hw))
  		return ERR_CAST(hw);
  	return hw->clk;
9d9f78ed9   Mike Turquette   clk: basic clock ...
191
  }
5cfe10bb0   Mike Turquette   clk: export fixed...
192
  EXPORT_SYMBOL_GPL(clk_register_gate);
4e3c021fb   Krzysztof Kozlowski   clk: Add clk_unre...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  
  void clk_unregister_gate(struct clk *clk)
  {
  	struct clk_gate *gate;
  	struct clk_hw *hw;
  
  	hw = __clk_get_hw(clk);
  	if (!hw)
  		return;
  
  	gate = to_clk_gate(hw);
  
  	clk_unregister(clk);
  	kfree(gate);
  }
  EXPORT_SYMBOL_GPL(clk_unregister_gate);
e270d8cb1   Stephen Boyd   clk: gate: Add hw...
209
210
211
212
213
214
215
216
217
218
219
  
  void clk_hw_unregister_gate(struct clk_hw *hw)
  {
  	struct clk_gate *gate;
  
  	gate = to_clk_gate(hw);
  
  	clk_hw_unregister(hw);
  	kfree(gate);
  }
  EXPORT_SYMBOL_GPL(clk_hw_unregister_gate);