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 if (common->features & CCU_FEATURE_UPDATE_BIT)
24 reg |= CCU_SUNXI_UPDATE_BIT;
25 writel(reg & ~gate, common->base + common->reg);
26
27 spin_unlock_irqrestore(common->lock, flags);
28 }
29 EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_disable, "SUNXI_CCU");
30
ccu_gate_disable(struct clk_hw * hw)31 static void ccu_gate_disable(struct clk_hw *hw)
32 {
33 struct ccu_gate *cg = hw_to_ccu_gate(hw);
34
35 return ccu_gate_helper_disable(&cg->common, cg->enable);
36 }
37
ccu_gate_helper_enable(struct ccu_common * common,u32 gate)38 int ccu_gate_helper_enable(struct ccu_common *common, u32 gate)
39 {
40 unsigned long flags;
41 u32 reg;
42
43 if (!gate)
44 return 0;
45
46 spin_lock_irqsave(common->lock, flags);
47
48 reg = readl(common->base + common->reg);
49 if (common->features & CCU_FEATURE_UPDATE_BIT)
50 reg |= CCU_SUNXI_UPDATE_BIT;
51 writel(reg | gate, common->base + common->reg);
52
53 spin_unlock_irqrestore(common->lock, flags);
54
55 return 0;
56 }
57 EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_enable, "SUNXI_CCU");
58
ccu_gate_enable(struct clk_hw * hw)59 static int ccu_gate_enable(struct clk_hw *hw)
60 {
61 struct ccu_gate *cg = hw_to_ccu_gate(hw);
62
63 return ccu_gate_helper_enable(&cg->common, cg->enable);
64 }
65
ccu_gate_helper_is_enabled(struct ccu_common * common,u32 gate)66 int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate)
67 {
68 if (!gate)
69 return 1;
70
71 return readl(common->base + common->reg) & gate;
72 }
73 EXPORT_SYMBOL_NS_GPL(ccu_gate_helper_is_enabled, "SUNXI_CCU");
74
ccu_gate_is_enabled(struct clk_hw * hw)75 static int ccu_gate_is_enabled(struct clk_hw *hw)
76 {
77 struct ccu_gate *cg = hw_to_ccu_gate(hw);
78
79 return ccu_gate_helper_is_enabled(&cg->common, cg->enable);
80 }
81
ccu_gate_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)82 static unsigned long ccu_gate_recalc_rate(struct clk_hw *hw,
83 unsigned long parent_rate)
84 {
85 struct ccu_gate *cg = hw_to_ccu_gate(hw);
86 unsigned long rate = parent_rate;
87
88 if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
89 rate /= cg->common.prediv;
90
91 return rate;
92 }
93
ccu_gate_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)94 static long ccu_gate_round_rate(struct clk_hw *hw, unsigned long rate,
95 unsigned long *prate)
96 {
97 struct ccu_gate *cg = hw_to_ccu_gate(hw);
98 int div = 1;
99
100 if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
101 div = cg->common.prediv;
102
103 if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
104 unsigned long best_parent = rate;
105
106 if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
107 best_parent *= div;
108 *prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
109 }
110
111 return *prate / div;
112 }
113
ccu_gate_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)114 static int ccu_gate_set_rate(struct clk_hw *hw, unsigned long rate,
115 unsigned long parent_rate)
116 {
117 /*
118 * We must report success but we can do so unconditionally because
119 * clk_factor_round_rate returns values that ensure this call is a
120 * nop.
121 */
122
123 return 0;
124 }
125
126 const struct clk_ops ccu_gate_ops = {
127 .disable = ccu_gate_disable,
128 .enable = ccu_gate_enable,
129 .is_enabled = ccu_gate_is_enabled,
130 .round_rate = ccu_gate_round_rate,
131 .set_rate = ccu_gate_set_rate,
132 .recalc_rate = ccu_gate_recalc_rate,
133 };
134 EXPORT_SYMBOL_NS_GPL(ccu_gate_ops, "SUNXI_CCU");
135