Merge branch 'for-next' of git://git.samba.org/sfrench/cifs-2.6
[sfrench/cifs-2.6.git] / drivers / clk / sunxi-ng / ccu_gate.c
1 /*
2  * Copyright (C) 2016 Maxime Ripard
3  * Maxime Ripard <maxime.ripard@free-electrons.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  */
10
11 #include <linux/clk-provider.h>
12
13 #include "ccu_gate.h"
14
15 void ccu_gate_helper_disable(struct ccu_common *common, u32 gate)
16 {
17         unsigned long flags;
18         u32 reg;
19
20         if (!gate)
21                 return;
22
23         spin_lock_irqsave(common->lock, flags);
24
25         reg = readl(common->base + common->reg);
26         writel(reg & ~gate, common->base + common->reg);
27
28         spin_unlock_irqrestore(common->lock, flags);
29 }
30
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
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         writel(reg | gate, common->base + common->reg);
50
51         spin_unlock_irqrestore(common->lock, flags);
52
53         return 0;
54 }
55
56 static int ccu_gate_enable(struct clk_hw *hw)
57 {
58         struct ccu_gate *cg = hw_to_ccu_gate(hw);
59
60         return ccu_gate_helper_enable(&cg->common, cg->enable);
61 }
62
63 int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate)
64 {
65         if (!gate)
66                 return 1;
67
68         return readl(common->base + common->reg) & gate;
69 }
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 };