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