1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2016 Maxime Ripard
4 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 */
6
7 #include <linux/clk-provider.h>
8 #include <linux/io.h>
9
10 #include "ccu_gate.h"
11
ccu_gate_helper_disable(struct ccu_common * common,u32 gate)12 void ccu_gate_helper_disable(struct ccu_common *common, u32 gate)
13 {
14 unsigned long flags;
15 u32 reg;
16
17 if (!gate)
18 return;
19
20 spin_lock_irqsave(common->lock, flags);
21
22 reg = readl(common->base + common->reg);
23 /* data reading result of the keyfield bits are always 0 */
24 if (common->features & CCU_FEATURE_KEY_FIELD_MOD) {
25 reg = reg | common->key_value;
26 }
27
28 writel(reg & ~gate, common->base + common->reg);
29
30 spin_unlock_irqrestore(common->lock, flags);
31 }
32
ccu_gate_disable(struct clk_hw * hw)33 static void ccu_gate_disable(struct clk_hw *hw)
34 {
35 struct ccu_gate *cg = hw_to_ccu_gate(hw);
36
37 return ccu_gate_helper_disable(&cg->common, cg->enable);
38 }
39
ccu_gate_helper_enable(struct ccu_common * common,u32 gate)40 int ccu_gate_helper_enable(struct ccu_common *common, u32 gate)
41 {
42 unsigned long flags;
43 u32 reg;
44
45 if (!gate)
46 return 0;
47
48 spin_lock_irqsave(common->lock, flags);
49
50 reg = readl(common->base + common->reg);
51
52 /* data reading result of the keyfield bits are always 0 */
53 if (common->features & CCU_FEATURE_KEY_FIELD_MOD) {
54 reg = reg | common->key_value;
55 }
56
57 writel(reg | gate, common->base + common->reg);
58
59 spin_unlock_irqrestore(common->lock, flags);
60
61 return 0;
62 }
63
ccu_gate_enable(struct clk_hw * hw)64 static int ccu_gate_enable(struct clk_hw *hw)
65 {
66 struct ccu_gate *cg = hw_to_ccu_gate(hw);
67
68 return ccu_gate_helper_enable(&cg->common, cg->enable);
69 }
70
ccu_gate_helper_is_enabled(struct ccu_common * common,u32 gate)71 int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate)
72 {
73 if (!gate)
74 return 1;
75
76 return readl(common->base + common->reg) & gate;
77 }
78
ccu_gate_is_enabled(struct clk_hw * hw)79 static int ccu_gate_is_enabled(struct clk_hw *hw)
80 {
81 struct ccu_gate *cg = hw_to_ccu_gate(hw);
82
83 return ccu_gate_helper_is_enabled(&cg->common, cg->enable);
84 }
85
ccu_gate_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)86 static unsigned long ccu_gate_recalc_rate(struct clk_hw *hw,
87 unsigned long parent_rate)
88 {
89 struct ccu_gate *cg = hw_to_ccu_gate(hw);
90 unsigned long rate = parent_rate;
91
92 if (cg->common.features & CCU_FEATURE_FIXED_RATE_GATE)
93 return cg->fixed_rate;
94
95 if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
96 rate /= cg->common.prediv;
97
98 return rate;
99 }
100
ccu_gate_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)101 static long ccu_gate_round_rate(struct clk_hw *hw, unsigned long rate,
102 unsigned long *prate)
103 {
104 struct ccu_gate *cg = hw_to_ccu_gate(hw);
105 int div = 1;
106
107 if (cg->common.features & CCU_FEATURE_FIXED_RATE_GATE)
108 return cg->fixed_rate;
109
110 if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
111 div = cg->common.prediv;
112
113 if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
114 unsigned long best_parent = rate;
115
116 if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
117 best_parent *= div;
118 *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
119 }
120
121 return *prate / div;
122 }
123
ccu_gate_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)124 static int ccu_gate_set_rate(struct clk_hw *hw, unsigned long rate,
125 unsigned long parent_rate)
126 {
127 /*
128 * We must report success but we can do so unconditionally because
129 * clk_factor_round_rate returns values that ensure this call is a
130 * nop.
131 */
132
133 return 0;
134 }
135
136 const struct clk_ops ccu_gate_ops = {
137 .disable = ccu_gate_disable,
138 .enable = ccu_gate_enable,
139 .is_enabled = ccu_gate_is_enabled,
140 .round_rate = ccu_gate_round_rate,
141 .set_rate = ccu_gate_set_rate,
142 .recalc_rate = ccu_gate_recalc_rate,
143 };
144 EXPORT_SYMBOL_GPL(ccu_gate_ops);
145